mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-24 03:39:11 +00:00
Compare commits
60 Commits
@now/build
...
@now/next@
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c71f672b3 | ||
|
|
dbc5f73984 | ||
|
|
1d269fffc8 | ||
|
|
cc146ba0f5 | ||
|
|
f8a2519838 | ||
|
|
1781376d47 | ||
|
|
d9fda14969 | ||
|
|
a4de9272e7 | ||
|
|
9b9037de91 | ||
|
|
8d18c65e3e | ||
|
|
e7d7de61b6 | ||
|
|
11927883c3 | ||
|
|
57d25b184b | ||
|
|
95f716fb3f | ||
|
|
8dd52605be | ||
|
|
4b9c6a2a2a | ||
|
|
17f92a5ad3 | ||
|
|
0aab7cc509 | ||
|
|
b39622b271 | ||
|
|
1e9aeee8e9 | ||
|
|
49fac0dfad | ||
|
|
a668df829f | ||
|
|
3d4ef1f825 | ||
|
|
f986daa1cc | ||
|
|
549c8777ba | ||
|
|
51d7242fda | ||
|
|
36db0e5bab | ||
|
|
99368b4248 | ||
|
|
95daf0e292 | ||
|
|
8bfa9c1a42 | ||
|
|
4208dc0466 | ||
|
|
00ae011b95 | ||
|
|
a770991a81 | ||
|
|
f80a6d6392 | ||
|
|
30777384ec | ||
|
|
0c719b7f6a | ||
|
|
ff18788b20 | ||
|
|
752ab39787 | ||
|
|
c1df8c8bd1 | ||
|
|
25fd1df35d | ||
|
|
d32ab1e0d9 | ||
|
|
a69c460760 | ||
|
|
b985853f15 | ||
|
|
94e607a93a | ||
|
|
f97a81fa14 | ||
|
|
6e28438eb4 | ||
|
|
8fcdf3f458 | ||
|
|
dbf0cc3562 | ||
|
|
27ccfa7e7a | ||
|
|
f37edbc670 | ||
|
|
b7943e83d2 | ||
|
|
300ed5b952 | ||
|
|
9e6ebfb3ec | ||
|
|
f49620790c | ||
|
|
84065688b5 | ||
|
|
5a1012fb0f | ||
|
|
4b6143c293 | ||
|
|
b6601b0d9a | ||
|
|
2870a1dd49 | ||
|
|
6249f7e293 |
6
.github/workflows/continuous-integration.yml
vendored
6
.github/workflows/continuous-integration.yml
vendored
@@ -45,6 +45,9 @@ jobs:
|
||||
- run: yarn install
|
||||
- run: yarn run build
|
||||
- run: yarn test-integration-once --clean false
|
||||
env:
|
||||
ZEIT_TEAM_TOKEN: ${{ secrets.ZEIT_TEAM_TOKEN }}
|
||||
ZEIT_REGISTRATION_URL: ${{ secrets.ZEIT_REGISTRATION_URL }}
|
||||
|
||||
test-now-cli:
|
||||
name: Now CLI Tests
|
||||
@@ -68,6 +71,9 @@ jobs:
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: yarn test-integration --clean false
|
||||
env:
|
||||
ZEIT_TEAM_TOKEN: ${{ secrets.ZEIT_TEAM_TOKEN }}
|
||||
ZEIT_REGISTRATION_URL: ${{ secrets.ZEIT_REGISTRATION_URL }}
|
||||
|
||||
test-now-dev:
|
||||
name: "`now dev` Tests"
|
||||
|
||||
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@@ -27,3 +27,5 @@ jobs:
|
||||
run: yarn publish-from-github
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
GA_TRACKING_ID: ${{ secrets.GA_TRACKING_ID }}
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
|
||||
115
examples/brunch/app/assets/brunch-napkin.svg
Normal file
115
examples/brunch/app/assets/brunch-napkin.svg
Normal file
@@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="256px" height="256px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve">
|
||||
<rect x="65" y="68" fill="#7C7C7C" width="127" height="124"/>
|
||||
<rect x="83.392" y="22.163" transform="matrix(-0.7071 0.7071 -0.7071 -0.7071 266.9294 23.9747)" fill="#86AD8A" width="90.215" height="90.215"/>
|
||||
<path fill="#FFFFFF" d="M117.601,167.637c3.788-11.191,5.795-45.647,4.728-62.795c-0.128-2.057-0.333-4.445-0.692-6.351
|
||||
c-0.112-0.607-0.244-1.168-0.391-1.651c0-6.672,0-62.892,0-67.02c0-5.717,5.242-8.419,10.166,4.128
|
||||
c4.925,12.547,3.495,57.175,3.495,60.348c0,0.673-0.611,1.469-1.57,2.25c-0.383,0.247-0.73,0.499-1.051,0.754
|
||||
c-2.949,2.366-3.379,5.095-3.533,7.542c-1.067,17.148-1.554,51.604,2.234,62.795c4.438,13.112-4.004,14.759-6.691,14.759
|
||||
C121.605,182.395,113.163,180.748,117.601,167.637z"/>
|
||||
<path fill="#FFFFFF" d="M103.019,47.413c-0.472,0.049-1.062,9.387-1.257,16.131c-0.238,8.194-0.607,15.482-2.908,15.482
|
||||
c-1.728,0-2.8-2.853-2.517-10.094c0.407-10.427,0.395-21.519-0.219-21.519c-0.774,0-4.358,22.999-4.358,31.09
|
||||
c0,5.803,2.487,11.269,5.603,14.115c0.01,0.008,0.019,0.019,0.033,0.027c0.093,0.081,0.191,0.166,0.278,0.247
|
||||
c0.005,0.004,0.01,0.004,0.01,0.008c2.252,2.053,2.819,4.283,2.971,6.32c1.286,17.143-1.128,59.614-5.678,70.804
|
||||
c-5.33,13.108,4.811,14.759,8.042,14.759c3.227,0,13.368-1.651,8.037-14.759c-4.55-11.191-6.96-53.661-5.678-70.804
|
||||
c0.153-2.037,0.719-4.267,2.971-6.32c0-0.004,0.005-0.004,0.01-0.008c0.088-0.081,0.185-0.166,0.278-0.247
|
||||
c0.014-0.008,0.023-0.019,0.033-0.027c3.116-2.845,5.764-8.314,5.604-14.115c-0.635-23.017-3.263-31.09-4.037-31.09
|
||||
c-0.614,0-0.613,12.286-0.438,21.024c0.143,7.158-0.471,10.383-2.49,10.383c-1.923,0-2.504-6.972-2.748-15.368
|
||||
c-0.196-6.762-0.804-16.039-1.54-16.039"/>
|
||||
<path fill="#FFFFFF" d="M154.075,179.591c-2.546,0-10.532-1.562-6.332-13.967c3.585-10.59,5.483-50.779,4.474-67.007
|
||||
c-0.051-0.826-0.256-1.664-0.413-2.55c-0.443-2.504-3.543-4.555-5.338-6.356c-3.486-3.498-5.725-9.38-5.725-16.013
|
||||
c0-10.77,5.97-24.4,13.334-24.4c7.364,0,13.334,13.63,13.334,24.4c0,6.633-2.24,12.515-5.725,16.013
|
||||
c-1.795,1.801-4.895,3.852-5.338,6.356c-0.157,0.886-0.361,1.724-0.413,2.55c-1.01,16.227,0.889,56.416,4.474,67.007
|
||||
C164.607,178.03,156.621,179.591,154.075,179.591z"/>
|
||||
<polyline fill="#CECCAE" points="65,68 65,192 192,192 "/>
|
||||
<polygon fill="#3F894A" points="65,191.835 65,253 192,253 192,191.835 192,66.938 "/>
|
||||
<g>
|
||||
<g>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.5" stroke-miterlimit="10" x1="191.658" y1="82.55" x2="190.946" y2="83.253"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.5" stroke-miterlimit="10" stroke-dasharray="2.02,2.02" x1="189.509" y1="84.672" x2="66.629" y2="206.035"/>
|
||||
|
||||
<line fill="none" stroke="#FFFFFF" stroke-width="0.5" stroke-miterlimit="10" x1="65.91" y1="206.745" x2="65.199" y2="207.448"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M85.353,211.117h6.328c0.827,0,1.665,0.067,2.513,0.201c0.848,0.135,1.608,0.414,2.28,0.838
|
||||
c0.672,0.424,1.22,1.014,1.644,1.768c0.424,0.756,0.636,1.753,0.636,2.994c0,1.116-0.331,2.089-0.993,2.916
|
||||
c-0.662,0.827-1.644,1.376-2.947,1.645v0.062c0.724,0.062,1.385,0.232,1.985,0.512c0.6,0.279,1.117,0.651,1.551,1.117
|
||||
c0.434,0.465,0.77,1.019,1.008,1.659c0.238,0.642,0.357,1.345,0.357,2.109c0,1.262-0.243,2.291-0.729,3.087
|
||||
c-0.486,0.797-1.097,1.422-1.83,1.877c-0.734,0.455-1.531,0.766-2.389,0.931c-0.858,0.166-1.66,0.248-2.404,0.248h-7.011V211.117z
|
||||
M87.4,220.609h4.25c1.055,0,1.913-0.129,2.575-0.388c0.662-0.259,1.179-0.574,1.551-0.946c0.373-0.372,0.621-0.771,0.745-1.194
|
||||
s0.186-0.812,0.186-1.163c0-0.765-0.129-1.401-0.388-1.908c-0.259-0.506-0.605-0.909-1.039-1.21
|
||||
c-0.435-0.299-0.941-0.512-1.52-0.636c-0.579-0.124-1.189-0.186-1.831-0.186H87.4V220.609z M87.4,231.219h4.684
|
||||
c1.199,0,2.166-0.145,2.9-0.435c0.734-0.289,1.298-0.651,1.691-1.085c0.393-0.435,0.656-0.895,0.791-1.381
|
||||
c0.134-0.485,0.202-0.926,0.202-1.318c0-0.807-0.155-1.499-0.465-2.078s-0.713-1.05-1.21-1.412c-0.496-0.361-1.06-0.625-1.69-0.791
|
||||
c-0.631-0.165-1.267-0.248-1.908-0.248H87.4V231.219z"/>
|
||||
<path fill="#FFFFFF" d="M103.73,222.781c0-0.269-0.011-0.595-0.031-0.978c-0.021-0.383-0.036-0.771-0.046-1.163
|
||||
c-0.011-0.393-0.026-0.76-0.047-1.102c-0.021-0.341-0.031-0.604-0.031-0.791h1.861c0.021,0.538,0.036,1.055,0.046,1.551
|
||||
c0.01,0.497,0.036,0.817,0.078,0.962c0.476-0.848,1.086-1.54,1.83-2.078c0.745-0.538,1.654-0.807,2.73-0.807
|
||||
c0.186,0,0.367,0.016,0.543,0.047c0.175,0.03,0.357,0.067,0.543,0.108l-0.217,1.83c-0.248-0.082-0.486-0.124-0.713-0.124
|
||||
c-0.807,0-1.504,0.129-2.094,0.388c-0.589,0.259-1.075,0.615-1.458,1.07s-0.667,0.987-0.853,1.598s-0.279,1.267-0.279,1.97v7.817
|
||||
h-1.861V222.781z"/>
|
||||
<path fill="#FFFFFF" d="M126,229.047c0,0.27,0.01,0.595,0.031,0.978c0.021,0.383,0.036,0.771,0.046,1.163
|
||||
c0.01,0.394,0.026,0.761,0.047,1.102c0.02,0.341,0.031,0.604,0.031,0.791h-1.861c-0.021-0.537-0.036-1.055-0.046-1.551
|
||||
c-0.011-0.497-0.036-0.817-0.078-0.962h-0.093c-0.373,0.786-0.993,1.463-1.861,2.032c-0.869,0.568-1.882,0.853-3.04,0.853
|
||||
c-1.117,0-2.032-0.176-2.746-0.527s-1.272-0.812-1.675-1.38c-0.403-0.569-0.678-1.226-0.822-1.971
|
||||
c-0.145-0.744-0.217-1.509-0.217-2.295v-8.531h1.861v8.438c0,0.58,0.051,1.144,0.155,1.691s0.284,1.039,0.543,1.474
|
||||
c0.258,0.434,0.621,0.78,1.086,1.039s1.07,0.388,1.815,0.388c0.682,0,1.329-0.119,1.938-0.356c0.61-0.238,1.137-0.6,1.582-1.086
|
||||
s0.796-1.097,1.055-1.83c0.258-0.734,0.388-1.598,0.388-2.591v-7.166H126V229.047z"/>
|
||||
<path fill="#FFFFFF" d="M130.584,222.781c0-0.269-0.01-0.595-0.029-0.978c-0.021-0.383-0.037-0.771-0.047-1.163
|
||||
c-0.012-0.393-0.027-0.76-0.047-1.102c-0.021-0.341-0.031-0.604-0.031-0.791h1.861c0.02,0.538,0.035,1.055,0.047,1.551
|
||||
c0.01,0.497,0.035,0.817,0.076,0.962h0.094c0.373-0.785,0.992-1.463,1.861-2.032c0.869-0.568,1.883-0.853,3.041-0.853
|
||||
c1.115,0,2.025,0.176,2.729,0.527s1.258,0.812,1.66,1.381c0.404,0.568,0.684,1.225,0.838,1.97c0.154,0.744,0.232,1.51,0.232,2.295
|
||||
v8.531h-1.861v-8.438c0-0.579-0.051-1.143-0.154-1.691c-0.104-0.547-0.285-1.039-0.543-1.473c-0.26-0.435-0.621-0.781-1.086-1.04
|
||||
c-0.465-0.258-1.07-0.388-1.814-0.388c-0.684,0-1.33,0.119-1.939,0.357c-0.609,0.237-1.137,0.6-1.582,1.085
|
||||
c-0.445,0.486-0.797,1.097-1.055,1.831c-0.26,0.734-0.389,1.598-0.389,2.59v7.166h-1.861V222.781z"/>
|
||||
<path fill="#FFFFFF" d="M159.28,230.971c-0.703,0.849-1.5,1.474-2.389,1.877c-0.89,0.403-1.852,0.604-2.885,0.604
|
||||
c-1.097,0-2.11-0.186-3.041-0.559c-0.931-0.372-1.727-0.894-2.389-1.566c-0.662-0.672-1.179-1.468-1.551-2.389
|
||||
c-0.372-0.92-0.559-1.928-0.559-3.024c0-1.096,0.187-2.104,0.559-3.024s0.889-1.717,1.551-2.389s1.458-1.194,2.389-1.566
|
||||
c0.931-0.373,1.944-0.559,3.041-0.559c1.055,0,2.031,0.217,2.931,0.651c0.9,0.435,1.701,1.045,2.404,1.83l-1.489,1.117
|
||||
c-0.537-0.6-1.127-1.07-1.768-1.412c-0.642-0.341-1.334-0.512-2.078-0.512c-0.869,0-1.655,0.155-2.358,0.466
|
||||
c-0.703,0.31-1.303,0.729-1.799,1.256s-0.874,1.148-1.133,1.861c-0.259,0.714-0.388,1.474-0.388,2.28s0.129,1.566,0.388,2.28
|
||||
c0.259,0.713,0.637,1.334,1.133,1.861s1.096,0.946,1.799,1.256c0.703,0.311,1.489,0.466,2.358,0.466
|
||||
c0.785,0,1.504-0.181,2.155-0.543c0.651-0.361,1.215-0.843,1.69-1.442L159.28,230.971z"/>
|
||||
<path fill="#FFFFFF" d="M161.925,209.628h1.861v11.633h0.062c0.372-0.785,0.992-1.463,1.861-2.032
|
||||
c0.868-0.568,1.882-0.853,3.04-0.853c1.116,0,2.026,0.176,2.729,0.527s1.257,0.812,1.66,1.381c0.403,0.568,0.683,1.225,0.837,1.97
|
||||
c0.155,0.744,0.233,1.51,0.233,2.295v8.531h-1.861v-8.438c0-0.579-0.052-1.143-0.155-1.691c-0.104-0.547-0.285-1.039-0.543-1.473
|
||||
c-0.259-0.435-0.62-0.781-1.086-1.04c-0.465-0.258-1.07-0.388-1.814-0.388c-0.683,0-1.329,0.119-1.939,0.357
|
||||
c-0.609,0.237-1.137,0.6-1.582,1.085c-0.444,0.486-0.796,1.097-1.055,1.831s-0.388,1.598-0.388,2.59v7.166h-1.861V209.628z"/>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.1 KiB |
@@ -10,7 +10,8 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="brunch">
|
||||
<a href="http://brunch.io"><img src="http://brunch.io/images/logo.png" alt="Brunch"></a>
|
||||
<p>Bon Appétit.</p>
|
||||
<img src="/brunch-napkin.svg" alt="Brunch">
|
||||
<h4>Bon Appétit.</h4>
|
||||
<h6>A <a href="http://brunch.io" target="_blank">Brunch</a> website deployed on <a href="https://zeit.co" target="_blank">ZEIT Now</a></h6>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.brunch {
|
||||
font-family: -apple-system, Sans-Serif;
|
||||
text-align: center;
|
||||
font-size: 24pt;
|
||||
color: #3f894a;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "gatsby-starter-default",
|
||||
"name": "gatsby",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"gatsby": "^2.18.14",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "gridsomee",
|
||||
"name": "gridsome",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "gridsome build",
|
||||
|
||||
@@ -79,4 +79,4 @@ DEPENDENCIES
|
||||
wdm (~> 0.1.0)
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.2
|
||||
2.1.4
|
||||
|
||||
@@ -105,4 +105,4 @@ DEPENDENCIES
|
||||
wdm (~> 0.1)
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.2
|
||||
2.1.4
|
||||
|
||||
4
examples/nextjs/.gitignore
vendored
4
examples/nextjs/.gitignore
vendored
@@ -24,3 +24,7 @@
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Environment Variables
|
||||
.env
|
||||
.env.build
|
||||
|
||||
4
now.json
4
now.json
@@ -3,6 +3,10 @@
|
||||
{
|
||||
"source": "/",
|
||||
"destination": "/api/frameworks"
|
||||
},
|
||||
{
|
||||
"source": "/api/v1/frameworks",
|
||||
"destination": "/api/frameworks"
|
||||
}
|
||||
],
|
||||
"env": {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"eslint": "6.2.2",
|
||||
"eslint-config-prettier": "6.1.0",
|
||||
"husky": "3.0.4",
|
||||
"json5": "2.1.1",
|
||||
"lint-staged": "9.2.5",
|
||||
"node-fetch": "2.6.0",
|
||||
"prettier": "1.18.2"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`build` from `package.json` or `next build`"
|
||||
"placeholder": "`npm run build` or `next build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "next dev --port $PORT"
|
||||
@@ -45,7 +45,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`gatsby build` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `gatsby build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "gatsby develop --port $PORT"
|
||||
@@ -62,6 +62,7 @@
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/hexo.svg",
|
||||
"tagline": "Hexo is a fast, simple & powerful blog framework powered by Node.js.",
|
||||
"description": "A Hexo site, created with the Hexo CLI.",
|
||||
"website": "https://hexo.io/",
|
||||
"detectors": {
|
||||
"every": [
|
||||
{
|
||||
@@ -72,7 +73,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`hexo generate` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `hexo generate`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "hexo server --port $PORT"
|
||||
@@ -89,6 +90,7 @@
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/eleventy.svg",
|
||||
"tagline": "11ty is a simpler static site generator written in JavaScript, created to be an alternative to Jekyll.",
|
||||
"description": "An Eleventy site, created with npm init.",
|
||||
"website": "https://www.11ty.dev/",
|
||||
"detectors": {
|
||||
"every": [
|
||||
{
|
||||
@@ -99,7 +101,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`npx @11ty/eleventy` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `npx @11ty/eleventy`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "npx @11ty/eleventy --serve --watch --port $PORT"
|
||||
@@ -116,6 +118,7 @@
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/docusaurus.svg",
|
||||
"tagline": "Docusaurus makes it easy to maintain Open Source documentation websites.",
|
||||
"description": "A static Docusaurus site that makes it easy to maintain OSS documentation.",
|
||||
"website": "https://docusaurus.io/",
|
||||
"detectors": {
|
||||
"some": [
|
||||
{
|
||||
@@ -130,7 +133,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`docusaurus-build` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `docusaurus-build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "docusaurus-start --port $PORT"
|
||||
@@ -158,7 +161,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`preact build` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `preact build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "preact watch --port $PORT"
|
||||
@@ -175,6 +178,7 @@
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/ember.svg",
|
||||
"tagline": "Ember.js helps webapp developers be more productive out of the box.",
|
||||
"description": "An Ember app, created with the Ember CLI.",
|
||||
"website": "https://emberjs.com/",
|
||||
"detectors": {
|
||||
"every": [
|
||||
{
|
||||
@@ -185,7 +189,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`ember build` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `ember build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "ember serve --port $PORT"
|
||||
@@ -213,7 +217,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`vue-cli-service build` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `vue-cli-service build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "vue-cli-service serve --port $PORT"
|
||||
@@ -229,6 +233,7 @@
|
||||
"demo": "https://scully.now-examples.now.sh",
|
||||
"tagline": "Scully is a static site generator for Angular.",
|
||||
"description": "The Static Site Generator for Angular apps.",
|
||||
"website": "https://github.com/scullyio/scully",
|
||||
"detectors": {
|
||||
"every": [
|
||||
{
|
||||
@@ -239,7 +244,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`ng build && scully` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `ng build && scully`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "ng serve --port $PORT"
|
||||
@@ -267,7 +272,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`ng build` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `ng build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "ng serve --port $PORT"
|
||||
@@ -284,6 +289,7 @@
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/polymer.svg",
|
||||
"tagline": "Polymer is an open-source webapps library from Google, for building using Web Components.",
|
||||
"description": "A Polymer app, created with the Polymer CLI.",
|
||||
"website": "https://www.polymer-project.org/",
|
||||
"detectors": {
|
||||
"every": [
|
||||
{
|
||||
@@ -294,7 +300,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`polymer build` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `polymer build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "polymer serve --port $PORT"
|
||||
@@ -322,7 +328,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`rollup -c` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `rollup -c`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "sirv public --single --dev --port $PORT"
|
||||
@@ -350,7 +356,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`build` from `package.json`"
|
||||
"placeholder": "npm run build"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "stencil build --dev --watch --serve --port $PORT"
|
||||
@@ -382,7 +388,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`react-scripts build` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `react-scripts build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "react-scripts start"
|
||||
@@ -399,6 +405,7 @@
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/gridsome.svg",
|
||||
"tagline": "Gridsome is a Vue.js-powered framework for building websites & apps that are fast by default.",
|
||||
"description": "A Gridsome app, created with the Gridsome CLI.",
|
||||
"website": "https://gridsome.org/",
|
||||
"detectors": {
|
||||
"every": [
|
||||
{
|
||||
@@ -409,7 +416,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`gridsome build` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `gridsome build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "gridsome develop -p $PORT"
|
||||
@@ -437,7 +444,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`umi build` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `umi build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "umi dev --port $PORT"
|
||||
@@ -465,7 +472,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`sapper export` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `sapper export`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "sapper dev --port $PORT"
|
||||
@@ -482,6 +489,7 @@
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/saber.svg",
|
||||
"tagline": "Saber is a framework for building static sites in Vue.js that supports data from any source.",
|
||||
"description": "A Saber site, created with npm init.",
|
||||
"website": "https://saber.land/",
|
||||
"detectors": {
|
||||
"every": [
|
||||
{
|
||||
@@ -492,7 +500,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`saber build` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `saber build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "saber --port $PORT"
|
||||
@@ -509,6 +517,7 @@
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/stencil.svg",
|
||||
"tagline": "Stencil is a powerful toolchain for building Progressive Web Apps and Design Systems.",
|
||||
"description": "A Stencil site, created with the Stencil CLI.",
|
||||
"website": "https://stenciljs.com/",
|
||||
"detectors": {
|
||||
"every": [
|
||||
{
|
||||
@@ -519,7 +528,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`stencil build` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `stencil build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "stencil build --dev --watch --serve --port $PORT"
|
||||
@@ -547,7 +556,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`nuxt build` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `nuxt build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "nuxt"
|
||||
@@ -580,7 +589,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"value": "hugo -D --gc"
|
||||
"placeholder": "`npm run build` or `hugo -D --gc`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "hugo server -D -w -p $PORT"
|
||||
@@ -597,6 +606,7 @@
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/jekyll.svg",
|
||||
"tagline": "Jekyll makes it super easy to transform your plain text into static websites and blogs.",
|
||||
"description": "A Jekyll site, created with the Jekyll CLI.",
|
||||
"website": "https://jekyllrb.com/",
|
||||
"detectors": {
|
||||
"every": [
|
||||
{
|
||||
@@ -606,7 +616,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"value": "jekyll build"
|
||||
"placeholder": "`npm run build` or `jekyll build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "bundle exec jekyll serve --watch --port $PORT"
|
||||
@@ -623,6 +633,7 @@
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/brunch.svg",
|
||||
"tagline": "Brunch is a fast and simple webapp build tool with seamless incremental compilation for rapid development.",
|
||||
"description": "A Brunch app, created with the Brunch CLI.",
|
||||
"website": "https://brunch.io/",
|
||||
"detectors": {
|
||||
"every": [
|
||||
{
|
||||
@@ -632,7 +643,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`brunch build --production` or `build` from `package.json`"
|
||||
"placeholder": "`npm run build` or `brunch build --production`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "brunch watch --server --port $PORT"
|
||||
@@ -649,6 +660,7 @@
|
||||
"logo": "https://raw.githubusercontent.com/zeit/now/master/packages/frameworks/logos/middleman.svg",
|
||||
"tagline": "Middleman is a static site generator that uses all the shortcuts and tools in modern web development.",
|
||||
"description": "A Middleman app, created with the Middleman CLI.",
|
||||
"website": "https://middlemanapp.com/",
|
||||
"detectors": {
|
||||
"every": [
|
||||
{
|
||||
@@ -658,7 +670,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"value": "bundle exec middleman build"
|
||||
"value": "`npm run build` or `bundle exec middleman build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"value": "bundle exec middleman server -p $PORT"
|
||||
@@ -675,7 +687,7 @@
|
||||
"description": "No framework or a unoptimized framework.",
|
||||
"settings": {
|
||||
"buildCommand": {
|
||||
"placeholder": "`build` or `now-build` from `package.json` if it exists"
|
||||
"placeholder": "`npm run now-build` or `npm run build`"
|
||||
},
|
||||
"devCommand": {
|
||||
"placeholder": "None"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/frameworks",
|
||||
"version": "0.0.11-canary.0",
|
||||
"version": "0.0.11",
|
||||
"main": "frameworks.json",
|
||||
"license": "UNLICENSED"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/build-utils",
|
||||
"version": "2.1.2-canary.0",
|
||||
"version": "2.2.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -99,6 +99,11 @@ export async function execCommand(command: string, options: SpawnOptions = {}) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function getNodeBinPath({ cwd }: { cwd: string }) {
|
||||
const { stdout } = await execAsync('npm', ['bin'], { cwd });
|
||||
return stdout.trim();
|
||||
}
|
||||
|
||||
async function chmodPlusX(fsPath: string) {
|
||||
const s = await fs.stat(fsPath);
|
||||
const newMode = s.mode | 64 | 8 | 1; // eslint-disable-line no-bitwise
|
||||
@@ -189,6 +194,46 @@ async function scanParentDirs(destPath: string, readPackageJson = false) {
|
||||
return { hasPackageLockJson, packageJson };
|
||||
}
|
||||
|
||||
interface WalkParentDirsProps {
|
||||
/**
|
||||
* The highest directory, typically the workPath root of the project.
|
||||
* If this directory is reached and it doesn't contain the file, null is returned.
|
||||
*/
|
||||
base: string;
|
||||
/**
|
||||
* The directory to start searching, typically the same directory of the entrypoint.
|
||||
* If this directory doesn't contain the file, the parent is checked, etc.
|
||||
*/
|
||||
start: string;
|
||||
/**
|
||||
* The name of the file to search for, typically `package.json` or `Gemfile`.
|
||||
*/
|
||||
filename: string;
|
||||
}
|
||||
|
||||
export async function walkParentDirs({
|
||||
base,
|
||||
start,
|
||||
filename,
|
||||
}: WalkParentDirsProps): Promise<string | null> {
|
||||
assert(path.isAbsolute(base), 'Expected "base" to be absolute path');
|
||||
assert(path.isAbsolute(start), 'Expected "start" to be absolute path');
|
||||
let parent = '';
|
||||
|
||||
for (let current = start; base.length <= current.length; current = parent) {
|
||||
const fullPath = path.join(current, filename);
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
if (await fs.pathExists(fullPath)) {
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
parent = path.dirname(current);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function runNpmInstall(
|
||||
destPath: string,
|
||||
args: string[] = [],
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
spawnAsync,
|
||||
execCommand,
|
||||
spawnCommand,
|
||||
walkParentDirs,
|
||||
installDependencies,
|
||||
runPackageJsonScript,
|
||||
runNpmInstall,
|
||||
@@ -20,6 +21,7 @@ import {
|
||||
runShellScript,
|
||||
getNodeVersion,
|
||||
getSpawnOptions,
|
||||
getNodeBinPath,
|
||||
} from './fs/run-user-scripts';
|
||||
import {
|
||||
getLatestNodeVersion,
|
||||
@@ -48,6 +50,8 @@ export {
|
||||
runPackageJsonScript,
|
||||
execCommand,
|
||||
spawnCommand,
|
||||
walkParentDirs,
|
||||
getNodeBinPath,
|
||||
runNpmInstall,
|
||||
runBundleInstall,
|
||||
runPipInstall,
|
||||
|
||||
@@ -18,7 +18,7 @@ module.exports = {
|
||||
{
|
||||
resolve: `gatsby-plugin-manifest`,
|
||||
options: {
|
||||
name: `gatsby-starter-default`,
|
||||
name: `05-zero-config-gatsby`,
|
||||
short_name: `starter`,
|
||||
start_url: `/`,
|
||||
background_color: `#663399`,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "gatsby-starter-default",
|
||||
"name": "05-zero-config-gatsby",
|
||||
"private": true,
|
||||
"description": "A simple starter to get up and developing quickly with Gatsby",
|
||||
"version": "0.1.0",
|
||||
|
||||
109
packages/now-build-utils/test/unit.walk.test.ts
vendored
Normal file
109
packages/now-build-utils/test/unit.walk.test.ts
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
import { walkParentDirs } from '../';
|
||||
import { strict } from 'assert';
|
||||
import { join } from 'path';
|
||||
import { promises } from 'fs';
|
||||
const { deepEqual, notDeepEqual, fail } = strict;
|
||||
const { readFile } = promises;
|
||||
const fixture = (name: string) => join(__dirname, 'walk', name);
|
||||
const filename = 'file.txt';
|
||||
|
||||
async function assertContent(target: string | null, contents: string) {
|
||||
notDeepEqual(target, null);
|
||||
const actual = await readFile(target!, 'utf8');
|
||||
deepEqual(actual.trim(), contents.trim());
|
||||
}
|
||||
|
||||
describe('Test `walkParentDirs`', () => {
|
||||
it('should throw when `base` is relative', async () => {
|
||||
const base = './relative';
|
||||
const start = __dirname;
|
||||
try {
|
||||
await walkParentDirs({ base, start, filename });
|
||||
fail('Expected error');
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
deepEqual(
|
||||
(error as Error).message,
|
||||
'Expected "base" to be absolute path'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw when `start` is relative', async () => {
|
||||
const base = __dirname;
|
||||
const start = './relative';
|
||||
try {
|
||||
await walkParentDirs({ base, start, filename });
|
||||
fail('Expected error');
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
deepEqual(
|
||||
(error as Error).message,
|
||||
'Expected "start" to be absolute path'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should find nested one', async () => {
|
||||
const base = fixture('every-directory');
|
||||
const start = base;
|
||||
const target = await walkParentDirs({ base, start, filename });
|
||||
await assertContent(target, 'First');
|
||||
});
|
||||
|
||||
it('should find nested two', async () => {
|
||||
const base = fixture('every-directory');
|
||||
const start = join(base, 'two');
|
||||
const target = await walkParentDirs({ base, start, filename });
|
||||
await assertContent(target, 'Second');
|
||||
});
|
||||
|
||||
it('should find nested three', async () => {
|
||||
const base = fixture('every-directory');
|
||||
const start = join(base, 'two', 'three');
|
||||
const target = await walkParentDirs({ base, start, filename });
|
||||
await assertContent(target, 'Third');
|
||||
});
|
||||
|
||||
it('should not find nested one', async () => {
|
||||
const base = fixture('not-found');
|
||||
const start = base;
|
||||
const target = await walkParentDirs({ base, start, filename });
|
||||
deepEqual(target, null);
|
||||
});
|
||||
|
||||
it('should not find nested two', async () => {
|
||||
const base = fixture('not-found');
|
||||
const start = join(base, 'two');
|
||||
const target = await walkParentDirs({ base, start, filename });
|
||||
deepEqual(target, null);
|
||||
});
|
||||
|
||||
it('should not find nested three', async () => {
|
||||
const base = fixture('not-found');
|
||||
const start = join(base, 'two', 'three');
|
||||
const target = await walkParentDirs({ base, start, filename });
|
||||
deepEqual(target, null);
|
||||
});
|
||||
|
||||
it('should find only one', async () => {
|
||||
const base = fixture('only-one');
|
||||
const start = join(base, 'two', 'three');
|
||||
const target = await walkParentDirs({ base, start, filename });
|
||||
await assertContent(target, 'First');
|
||||
});
|
||||
|
||||
it('should find only two', async () => {
|
||||
const base = fixture('only-two');
|
||||
const start = join(base, 'two', 'three');
|
||||
const target = await walkParentDirs({ base, start, filename });
|
||||
await assertContent(target, 'Second');
|
||||
});
|
||||
|
||||
it('should find only three', async () => {
|
||||
const base = fixture('only-three');
|
||||
const start = join(base, 'two', 'three');
|
||||
const target = await walkParentDirs({ base, start, filename });
|
||||
await assertContent(target, 'Third');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
Another
|
||||
@@ -0,0 +1 @@
|
||||
First
|
||||
@@ -0,0 +1 @@
|
||||
Another
|
||||
@@ -0,0 +1 @@
|
||||
Second
|
||||
@@ -0,0 +1 @@
|
||||
Another
|
||||
@@ -0,0 +1 @@
|
||||
Third
|
||||
1
packages/now-build-utils/test/walk/not-found/another.txt
Normal file
1
packages/now-build-utils/test/walk/not-found/another.txt
Normal file
@@ -0,0 +1 @@
|
||||
Another
|
||||
@@ -0,0 +1 @@
|
||||
Another
|
||||
@@ -0,0 +1 @@
|
||||
Another
|
||||
1
packages/now-build-utils/test/walk/only-one/another.txt
Normal file
1
packages/now-build-utils/test/walk/only-one/another.txt
Normal file
@@ -0,0 +1 @@
|
||||
Another
|
||||
1
packages/now-build-utils/test/walk/only-one/file.txt
Normal file
1
packages/now-build-utils/test/walk/only-one/file.txt
Normal file
@@ -0,0 +1 @@
|
||||
First
|
||||
@@ -0,0 +1 @@
|
||||
Another
|
||||
@@ -0,0 +1 @@
|
||||
Another
|
||||
@@ -0,0 +1 @@
|
||||
Another
|
||||
@@ -0,0 +1 @@
|
||||
Another
|
||||
@@ -0,0 +1 @@
|
||||
Another
|
||||
@@ -0,0 +1 @@
|
||||
Third
|
||||
1
packages/now-build-utils/test/walk/only-two/another.txt
Normal file
1
packages/now-build-utils/test/walk/only-two/another.txt
Normal file
@@ -0,0 +1 @@
|
||||
Another
|
||||
@@ -0,0 +1 @@
|
||||
Another
|
||||
1
packages/now-build-utils/test/walk/only-two/two/file.txt
Normal file
1
packages/now-build-utils/test/walk/only-two/two/file.txt
Normal file
@@ -0,0 +1 @@
|
||||
Second
|
||||
@@ -0,0 +1 @@
|
||||
Another
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@now/cgi",
|
||||
"version": "1.0.4-canary.0",
|
||||
"version": "1.0.4",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "now",
|
||||
"version": "18.0.0-canary.6",
|
||||
"version": "17.1.0",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Now",
|
||||
@@ -14,6 +14,7 @@
|
||||
"preinstall": "node ./scripts/preinstall.js",
|
||||
"test-unit": "nyc ava test/unit.js test/dev-builder.unit.js test/dev-router.unit.js test/dev-server.unit.js --serial --fail-fast --verbose",
|
||||
"test-integration": "ava test/integration.js --serial --fail-fast",
|
||||
"test-integration-v1": "ava test/integration-v1.js --serial --fail-fast",
|
||||
"test-integration-now-dev": "ava test/dev/integration.js --serial --fail-fast --verbose",
|
||||
"prepublishOnly": "yarn build",
|
||||
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
|
||||
@@ -91,10 +92,12 @@
|
||||
"@types/universal-analytics": "0.4.2",
|
||||
"@types/which": "1.3.2",
|
||||
"@types/write-json-file": "2.2.1",
|
||||
"@zeit/dockerignore": "0.0.5",
|
||||
"@zeit/fun": "0.11.2",
|
||||
"@zeit/ncc": "0.18.5",
|
||||
"@zeit/source-map-support": "0.6.2",
|
||||
"ajv": "6.10.2",
|
||||
"alpha-sort": "2.0.1",
|
||||
"ansi-escapes": "3.0.0",
|
||||
"ansi-regex": "3.0.0",
|
||||
"arg": "2.0.0",
|
||||
@@ -127,6 +130,8 @@
|
||||
"get-port": "5.1.1",
|
||||
"glob": "7.1.2",
|
||||
"http-proxy": "1.17.0",
|
||||
"ignore": "4.0.6",
|
||||
"ini": "1.3.4",
|
||||
"inquirer": "7.0.4",
|
||||
"is-port-reachable": "3.0.0",
|
||||
"is-url": "1.2.2",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { handleError } from '../../util/error';
|
||||
@@ -30,11 +31,15 @@ const help = () => {
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.now`'} directory
|
||||
-r ${chalk.bold.underline('RULES_FILE')}, --rules=${chalk.bold.underline(
|
||||
'RULES_FILE'
|
||||
)} Rules file
|
||||
-d, --debug Debug mode [off]
|
||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
-S, --scope Set a custom scope
|
||||
-n, --no-verify Don't wait until instance count meets the previous alias constraints
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
@@ -60,6 +65,22 @@ const help = () => {
|
||||
${chalk.dim('–')} ${chalk.dim(
|
||||
'Protocols'
|
||||
)} in the URLs are unneeded and ignored.
|
||||
|
||||
${chalk.gray('–')} Add and modify path based aliases for ${chalk.underline(
|
||||
'zeit.ninja'
|
||||
)}
|
||||
|
||||
${chalk.cyan(
|
||||
`$ now alias ${chalk.underline('zeit.ninja')} -r ${chalk.underline(
|
||||
'rules.json'
|
||||
)}`
|
||||
)}
|
||||
|
||||
Export effective routing rules
|
||||
|
||||
${chalk.cyan(
|
||||
`$ now alias ls aliasId --json > ${chalk.underline('rules.json')}`
|
||||
)}
|
||||
`);
|
||||
};
|
||||
|
||||
@@ -76,7 +97,11 @@ export default async function main(ctx) {
|
||||
try {
|
||||
argv = getArgs(ctx.argv.slice(2), {
|
||||
'--json': Boolean,
|
||||
'--no-verify': Boolean,
|
||||
'--rules': String,
|
||||
'--yes': Boolean,
|
||||
'-n': '--no-verify',
|
||||
'-r': '--rules',
|
||||
'-y': '--yes',
|
||||
});
|
||||
} catch (err) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import plural from 'pluralize';
|
||||
import table from 'text-table';
|
||||
import Now from '../../util/now';
|
||||
import Now from '../../util';
|
||||
import Client from '../../util/client.ts';
|
||||
import getAliases from '../../util/alias/get-aliases';
|
||||
import getScope from '../../util/get-scope.ts';
|
||||
@@ -36,6 +36,7 @@ export default async function ls(ctx, opts, args, output) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// $FlowFixMe
|
||||
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
|
||||
const lsStamp = stamp();
|
||||
let cancelWait;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import table from 'text-table';
|
||||
import Now from '../../util/now';
|
||||
import Now from '../../util';
|
||||
import cmd from '../../util/output/cmd.ts';
|
||||
import Client from '../../util/client.ts';
|
||||
import getScope from '../../util/get-scope.ts';
|
||||
@@ -39,6 +39,7 @@ export default async function rm(ctx, opts, args, output) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// $FlowFixMe
|
||||
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
|
||||
const [aliasOrId] = args;
|
||||
|
||||
|
||||
@@ -1,24 +1,34 @@
|
||||
import ms from 'ms';
|
||||
import chalk from 'chalk';
|
||||
import { SetDifference } from 'utility-types';
|
||||
import { AliasRecord } from '../../util/alias/create-alias';
|
||||
import { NowContext, Domain } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import * as ERRORS from '../../util/errors';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import assignAlias from '../../util/alias/assign-alias';
|
||||
import Client from '../../util/client';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import formatDnsTable from '../../util/format-dns-table';
|
||||
import formatNSTable from '../../util/format-ns-table';
|
||||
import getDeploymentByIdOrHost from '../../util/deploy/get-deployment-by-id-or-host';
|
||||
import getDeploymentForAlias from '../../util/alias/get-deployment-for-alias';
|
||||
import getRulesFromFile from '../../util/alias/get-rules-from-file';
|
||||
import getScope from '../../util/get-scope';
|
||||
import { getTargetsForAlias } from '../../util/alias/get-targets-for-alias';
|
||||
import humanizePath from '../../util/humanize-path';
|
||||
import setupDomain from '../../util/domains/setup-domain';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import { isValidName } from '../../util/is-valid-name';
|
||||
import upsertPathAlias from '../../util/alias/upsert-path-alias';
|
||||
import handleCertError from '../../util/certs/handle-cert-error';
|
||||
import isWildcardAlias from '../../util/alias/is-wildcard-alias';
|
||||
import link from '../../util/output/link';
|
||||
import { User } from '../../types';
|
||||
|
||||
type Options = {
|
||||
'--debug': boolean;
|
||||
'--local-config': string;
|
||||
'--no-verify': boolean;
|
||||
'--rules': string;
|
||||
};
|
||||
|
||||
export default async function set(
|
||||
@@ -30,13 +40,18 @@ export default async function set(
|
||||
const {
|
||||
authConfig: { token },
|
||||
config,
|
||||
localConfig,
|
||||
} = ctx;
|
||||
|
||||
const { currentTeam } = config;
|
||||
const { apiUrl } = ctx;
|
||||
const setStamp = stamp();
|
||||
|
||||
const { '--debug': debugEnabled } = opts;
|
||||
const {
|
||||
'--debug': debugEnabled,
|
||||
'--no-verify': noVerify,
|
||||
'--rules': rulesPath,
|
||||
} = opts;
|
||||
|
||||
const client = new Client({
|
||||
apiUrl,
|
||||
@@ -44,10 +59,12 @@ export default async function set(
|
||||
currentTeam,
|
||||
debug: debugEnabled,
|
||||
});
|
||||
let contextName = null;
|
||||
|
||||
let user: User;
|
||||
let contextName: string | null = null;
|
||||
|
||||
try {
|
||||
({ contextName } = await getScope(client));
|
||||
({ contextName, user } = await getScope(client));
|
||||
} catch (err) {
|
||||
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
|
||||
output.error(err.message);
|
||||
@@ -77,7 +94,35 @@ export default async function set(
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (args.length === 0) {
|
||||
// Read the path alias rules in case there is is given
|
||||
const rules = await getRulesFromFile(rulesPath);
|
||||
if (rules instanceof ERRORS.FileNotFound) {
|
||||
output.error(`Can't find the provided rules file at location:`);
|
||||
output.print(` ${chalk.gray('-')} ${rules.meta.file}\n`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rules instanceof ERRORS.CantParseJSONFile) {
|
||||
output.error(`Error parsing provided rules.json file at location:`);
|
||||
output.print(` ${chalk.gray('-')} ${rules.meta.file}\n`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rules instanceof ERRORS.RulesFileValidationError) {
|
||||
output.error(`Path Alias validation error: ${rules.meta.message}`);
|
||||
output.print(` ${chalk.gray('-')} ${rules.meta.location}\n`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If the user provided rules and also a deployment target, we should fail
|
||||
if (args.length === 2 && rules) {
|
||||
output.error(
|
||||
`You can't supply a deployment target and target rules simultaneously.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (args.length === 0 && !rules) {
|
||||
output.error(
|
||||
`To ship to production, optionally configure your domains (${link(
|
||||
'https://zeit.co/docs/v2/custom-domains/'
|
||||
@@ -86,11 +131,62 @@ export default async function set(
|
||||
return 1;
|
||||
}
|
||||
|
||||
const [deploymentIdOrHost, aliasTarget] = args;
|
||||
// Find the targets to perform the alias
|
||||
const targets = getTargetsForAlias(args, localConfig);
|
||||
|
||||
if (targets instanceof ERRORS.NoAliasInConfig) {
|
||||
output.error(`Couldn't find an alias in config`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (targets instanceof ERRORS.InvalidAliasInConfig) {
|
||||
output.error(
|
||||
`Wrong value for alias found in config. It must be a string or array of string.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (rules) {
|
||||
// If we have rules for path alias we assign them to the domain
|
||||
for (const target of targets) {
|
||||
output.log(
|
||||
`Assigning path alias rules from ${humanizePath(
|
||||
rulesPath
|
||||
)} to ${target}`
|
||||
);
|
||||
const pathAlias = await upsertPathAlias(
|
||||
output,
|
||||
client,
|
||||
rules,
|
||||
target,
|
||||
contextName
|
||||
);
|
||||
const remaining = handleCreateAliasError(output, pathAlias);
|
||||
if (handleSetupDomainError(output, remaining) !== 1) {
|
||||
console.log(
|
||||
`${chalk.cyan('> Success!')} ${
|
||||
rules.length
|
||||
} rules configured for ${chalk.underline(target)} ${setStamp()}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If there are no rules for path alias we should find out a deployment and perform the alias
|
||||
|
||||
const deployment = handleCertError(
|
||||
output,
|
||||
await getDeploymentByIdOrHost(client, contextName, deploymentIdOrHost)
|
||||
await getDeploymentForAlias(
|
||||
client,
|
||||
output,
|
||||
args,
|
||||
opts['--local-config'],
|
||||
user,
|
||||
contextName,
|
||||
localConfig
|
||||
)
|
||||
);
|
||||
|
||||
if (deployment === 1) {
|
||||
@@ -127,34 +223,37 @@ export default async function set(
|
||||
return 1;
|
||||
}
|
||||
|
||||
output.log(`Assigning alias ${aliasTarget} to deployment ${deployment.url}`);
|
||||
// Assign the alias for each of the targets in the array
|
||||
for (const target of targets) {
|
||||
output.log(`Assigning alias ${target} to deployment ${deployment.url}`);
|
||||
|
||||
const isWildcard = isWildcardAlias(aliasTarget);
|
||||
const record = await assignAlias(
|
||||
output,
|
||||
client,
|
||||
deployment,
|
||||
aliasTarget,
|
||||
contextName
|
||||
);
|
||||
const isWildcard = isWildcardAlias(target);
|
||||
const record = await assignAlias(
|
||||
output,
|
||||
client,
|
||||
deployment,
|
||||
target,
|
||||
contextName,
|
||||
noVerify
|
||||
);
|
||||
const handleResult = handleSetupDomainError(
|
||||
output,
|
||||
handleCreateAliasError(output, record),
|
||||
isWildcard
|
||||
);
|
||||
if (handleResult === 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const handleResult = handleSetupDomainError(
|
||||
output,
|
||||
handleCreateAliasError(output, record)
|
||||
);
|
||||
const prefix = isWildcard ? '' : 'https://';
|
||||
|
||||
if (handleResult === 1) {
|
||||
return 1;
|
||||
console.log(
|
||||
`${chalk.cyan('> Success!')} ${chalk.bold(
|
||||
`${prefix}${handleResult.alias}`
|
||||
)} now points to https://${deployment.url} ${setStamp()}`
|
||||
);
|
||||
}
|
||||
|
||||
const prefix = isWildcard ? '' : 'https://';
|
||||
|
||||
console.log(
|
||||
`${chalk.cyan('> Success!')} ${chalk.bold(
|
||||
`${prefix}${handleResult.alias}`
|
||||
)} now points to https://${deployment.url} ${setStamp()}`
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -164,7 +263,8 @@ type SetupDomainError = Exclude<SetupDomainResolve, Domain>;
|
||||
|
||||
function handleSetupDomainError<T>(
|
||||
output: Output,
|
||||
error: SetupDomainError | T
|
||||
error: SetupDomainError | T,
|
||||
isWildcard: boolean = false
|
||||
): T | 1 {
|
||||
if (
|
||||
error instanceof ERRORS.DomainVerificationFailed ||
|
||||
@@ -176,7 +276,9 @@ function handleSetupDomainError<T>(
|
||||
`We could not alias since the domain ${domain} could not be verified due to the following reasons:\n`
|
||||
);
|
||||
output.print(
|
||||
`Nameservers verification failed since we see a different set than the intended set:`
|
||||
` ${chalk.gray(
|
||||
'a)'
|
||||
)} Nameservers verification failed since we see a different set than the intended set:`
|
||||
);
|
||||
output.print(
|
||||
`\n${formatNSTable(
|
||||
@@ -185,6 +287,34 @@ function handleSetupDomainError<T>(
|
||||
{ extraSpace: ' ' }
|
||||
)}\n\n`
|
||||
);
|
||||
if (error instanceof ERRORS.DomainVerificationFailed && !isWildcard) {
|
||||
const { txtVerification } = error.meta;
|
||||
output.print(
|
||||
` ${chalk.gray(
|
||||
'b)'
|
||||
)} DNS TXT verification failed since found no matching records.`
|
||||
);
|
||||
output.print(
|
||||
`\n${formatDnsTable(
|
||||
[['_now', 'TXT', txtVerification.verificationRecord]],
|
||||
{ extraSpace: ' ' }
|
||||
)}\n\n`
|
||||
);
|
||||
output.print(
|
||||
` Once your domain uses either the nameservers or the TXT DNS record from above, run again ${cmd(
|
||||
'now domains verify <domain>'
|
||||
)}.\n`
|
||||
);
|
||||
output.print(
|
||||
` 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');
|
||||
return 1;
|
||||
}
|
||||
@@ -325,6 +455,66 @@ function handleCreateAliasError<T>(
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (error instanceof ERRORS.RuleValidationFailed) {
|
||||
output.error(`Rule validation error: ${error.meta.message}.`);
|
||||
output.print(` Make sure your rules file is written correctly.\n`);
|
||||
return 1;
|
||||
}
|
||||
if (error instanceof ERRORS.VerifyScaleTimeout) {
|
||||
output.error(`Instance verification timed out (${ms(error.meta.timeout)})`);
|
||||
output.log('Read more: https://err.sh/now/verification-timeout');
|
||||
return 1;
|
||||
}
|
||||
if (error instanceof ERRORS.NotSupportedMinScaleSlots) {
|
||||
output.error(
|
||||
`Scale rules from previous aliased deployment ${chalk.dim(
|
||||
error.meta.url
|
||||
)} could not be copied since Cloud v2 deployments cannot have a non-zero min`
|
||||
);
|
||||
output.log(
|
||||
`Update the scale settings on ${chalk.dim(
|
||||
error.meta.url
|
||||
)} with \`now scale\` and try again`
|
||||
);
|
||||
output.log('Read more: https://err.sh/now/v2-no-min');
|
||||
return 1;
|
||||
}
|
||||
if (error instanceof ERRORS.ForbiddenScaleMaxInstances) {
|
||||
output.error(
|
||||
`Scale rules from previous aliased deployment ${chalk.dim(
|
||||
error.meta.url
|
||||
)} could not be copied since the given number of max instances (${
|
||||
error.meta.max
|
||||
}) is not allowed.`
|
||||
);
|
||||
output.log(
|
||||
`Update the scale settings on ${chalk.dim(
|
||||
error.meta.url
|
||||
)} with \`now scale\` and try again`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
if (error instanceof ERRORS.ForbiddenScaleMinInstances) {
|
||||
output.error(
|
||||
`You can't scale to more than ${error.meta.max} min instances with your current plan.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (error instanceof ERRORS.InvalidScaleMinMaxRelation) {
|
||||
output.error(
|
||||
`Scale rules from previous aliased deployment ${chalk.dim(
|
||||
error.meta.url
|
||||
)} could not be copied becuase the relation between min and max instances is wrong.`
|
||||
);
|
||||
output.log(
|
||||
`Update the scale settings on ${chalk.dim(
|
||||
error.meta.url
|
||||
)} with \`now scale\` and try again`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (error instanceof ERRORS.CertMissing) {
|
||||
output.error(
|
||||
`There is no certificate for the domain ${error.meta.domain} and it could not be created.`
|
||||
|
||||
@@ -300,13 +300,10 @@ async function run({ token, config: { currentTeam } }) {
|
||||
const deletedCard = cards.sources.find(card => card.id === cardId);
|
||||
const remainingCards = cards.sources.filter(card => card.id !== cardId);
|
||||
|
||||
let text = `The provided card does not exist`;
|
||||
|
||||
if (deletedCard) {
|
||||
text = `${deletedCard.brand ||
|
||||
deletedCard.card.brand} ending in ${deletedCard.last4 ||
|
||||
deletedCard.card.last4} was deleted`;
|
||||
}
|
||||
let text = `${deletedCard.brand ||
|
||||
deletedCard.card.brand} ending in ${deletedCard.last4 ||
|
||||
deletedCard.card.last4} was deleted`;
|
||||
// ${chalk.gray(`[${elapsed}]`)}
|
||||
|
||||
if (cardId === cards.defaultSource) {
|
||||
if (remainingCards.length === 0) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import chalk from 'chalk';
|
||||
import Now from '../../util/now';
|
||||
|
||||
// @ts-ignore
|
||||
import Now from '../../util';
|
||||
import Client from '../../util/client';
|
||||
import getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
@@ -59,6 +61,7 @@ async function add(
|
||||
throw err;
|
||||
}
|
||||
|
||||
// $FlowFixMe
|
||||
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
|
||||
|
||||
if (overwite) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import chalk from 'chalk';
|
||||
|
||||
import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import * as ERRORS from '../../util/errors';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import Client from '../../util/client';
|
||||
import createCertForCns from '../../util/certs/create-cert-for-cns';
|
||||
import createCertFromFile from '../../util/certs/create-cert-from-file';
|
||||
|
||||
@@ -3,13 +3,14 @@ import ms from 'ms';
|
||||
import plural from 'pluralize';
|
||||
import psl from 'psl';
|
||||
import table from 'text-table';
|
||||
import Now from '../../util/now';
|
||||
// @ts-ignore
|
||||
import Now from '../../util';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import Client from '../../util/client';
|
||||
import getScope from '../../util/get-scope';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import getCerts from '../../util/certs/get-certs';
|
||||
import { CertNotFound } from '../../util/errors';
|
||||
import { CertNotFound } from '../../util/errors-ts';
|
||||
import strlen from '../../util/strlen';
|
||||
import { Output } from '../../util/output';
|
||||
import { NowContext, Cert } from '../../types';
|
||||
|
||||
@@ -3,7 +3,7 @@ import ms from 'ms';
|
||||
import plural from 'pluralize';
|
||||
import table from 'text-table';
|
||||
import { NowContext, Cert } from '../../types';
|
||||
import * as ERRORS from '../../util/errors';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import { Output } from '../../util/output';
|
||||
import deleteCertById from '../../util/certs/delete-cert-by-id';
|
||||
import getCertById from '../../util/certs/get-cert-by-id';
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import chalk from 'chalk';
|
||||
import logo from '../../util/output/logo';
|
||||
import code from '../../util/output/code';
|
||||
import note from '../../util/output/note';
|
||||
|
||||
export const help = () => `
|
||||
export const latestHelp = () => `
|
||||
${chalk.bold(`${logo} now`)} [options] <command | path>
|
||||
|
||||
${chalk.dim('Commands:')}
|
||||
@@ -35,6 +37,7 @@ export const help = () => `
|
||||
|
||||
-h, --help Output usage information
|
||||
-v, --version Output the version number
|
||||
-V, --platform-version Set the platform version to deploy to
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`now.json`'} file
|
||||
@@ -64,6 +67,12 @@ export const help = () => `
|
||||
--prod Create a production deployment
|
||||
-c, --confirm Confirm default options and skip questions
|
||||
|
||||
${note(
|
||||
`To view the usage information for Now 1.0, run ${code(
|
||||
'now help deploy-v1'
|
||||
)}`
|
||||
)}
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Deploy the current directory
|
||||
@@ -86,13 +95,14 @@ export const help = () => `
|
||||
|
||||
`;
|
||||
|
||||
export const args = {
|
||||
export const latestArgs = {
|
||||
'--force': Boolean,
|
||||
'--public': Boolean,
|
||||
'--no-clipboard': Boolean,
|
||||
'--env': [String],
|
||||
'--build-env': [String],
|
||||
'--meta': [String],
|
||||
'--no-scale': Boolean,
|
||||
// This is not an array in favor of matching
|
||||
// the config property name.
|
||||
'--regions': String,
|
||||
@@ -111,3 +121,180 @@ export const args = {
|
||||
'-n': '--name',
|
||||
'--target': String,
|
||||
};
|
||||
|
||||
export const legacyArgsMri = {
|
||||
string: [
|
||||
'name',
|
||||
'build-env',
|
||||
'alias',
|
||||
'meta',
|
||||
'session-affinity',
|
||||
'regions',
|
||||
'dotenv',
|
||||
'target',
|
||||
],
|
||||
boolean: [
|
||||
'help',
|
||||
'version',
|
||||
'debug',
|
||||
'force',
|
||||
'links',
|
||||
'C',
|
||||
'clipboard',
|
||||
'forward-npm',
|
||||
'docker',
|
||||
'npm',
|
||||
'static',
|
||||
'public',
|
||||
'no-scale',
|
||||
'no-verify',
|
||||
'dotenv',
|
||||
'prod',
|
||||
],
|
||||
default: {
|
||||
C: false,
|
||||
clipboard: true,
|
||||
},
|
||||
alias: {
|
||||
env: 'e',
|
||||
meta: 'm',
|
||||
'build-env': 'b',
|
||||
dotenv: 'E',
|
||||
help: 'h',
|
||||
debug: 'd',
|
||||
version: 'v',
|
||||
force: 'f',
|
||||
links: 'l',
|
||||
public: 'p',
|
||||
'forward-npm': 'N',
|
||||
'session-affinity': 'S',
|
||||
name: 'n',
|
||||
project: 'P',
|
||||
alias: 'a',
|
||||
},
|
||||
};
|
||||
|
||||
// The following arg parsing is simply to make it compatible
|
||||
// with the index. Let's not migrate it to the new args parsing, as
|
||||
// we are gonna delete this file soon anyways.
|
||||
const argList = {};
|
||||
|
||||
for (const item of legacyArgsMri.string) {
|
||||
argList[`--${item}`] = String;
|
||||
}
|
||||
|
||||
for (const item of legacyArgsMri.boolean) {
|
||||
argList[`--${item}`] = Boolean;
|
||||
}
|
||||
|
||||
for (const item of Object.keys(legacyArgsMri.alias)) {
|
||||
argList[`-${legacyArgsMri.alias[item]}`] = `--${item}`;
|
||||
}
|
||||
|
||||
export const legacyArgs = argList;
|
||||
|
||||
export const legacyHelp = () => `
|
||||
${chalk.bold(`${logo} now`)} [options] <command | path>
|
||||
|
||||
${chalk.dim('Commands:')}
|
||||
|
||||
${chalk.dim('Cloud')}
|
||||
|
||||
deploy [path] Performs a deployment ${chalk.bold(
|
||||
'(default)'
|
||||
)}
|
||||
ls | list [app] Lists deployments
|
||||
rm | remove [id] Removes a deployment
|
||||
ln | alias [id] [url] Configures aliases for deployments
|
||||
inspect [id] Displays information related to a deployment
|
||||
domains [name] Manages your domain names
|
||||
certs [cmd] Manages your SSL certificates
|
||||
secrets [name] Manages your secret environment variables
|
||||
dns [name] Manages your DNS records
|
||||
logs [url] Displays the logs for a deployment
|
||||
scale [args] Scales the instance count of a deployment
|
||||
init [example] Initialize an example project
|
||||
help [cmd] Displays complete help for [cmd]
|
||||
|
||||
${chalk.dim('Administrative')}
|
||||
|
||||
billing | cc [cmd] Manages your credit cards and billing methods
|
||||
upgrade | downgrade [plan] Upgrades or downgrades your plan
|
||||
teams Manages your teams
|
||||
switch [scope] Switches between teams and your account
|
||||
login [email] Logs into your account or creates a new one
|
||||
logout Logs out of your account
|
||||
whoami Shows the username of the currently logged in user
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-v, --version Output the version number
|
||||
-V, --platform-version Set the platform version to deploy to
|
||||
-n, --name Set the project name of the deployment
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`now.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.now`'} directory
|
||||
-d, --debug Debug mode [off]
|
||||
-f, --force Force a new deployment even if nothing has changed
|
||||
-t ${chalk.underline('TOKEN')}, --token=${chalk.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
-l, --links Copy symlinks without resolving their target
|
||||
-p, --public Deployment is public (${chalk.dim(
|
||||
'`/_src`'
|
||||
)} is exposed) [on for oss, off for premium]
|
||||
-e, --env Include an env var during run time (e.g.: ${chalk.dim(
|
||||
'`-e KEY=value`'
|
||||
)}). Can appear many times.
|
||||
-b, --build-env Similar to ${chalk.dim(
|
||||
'`--env`'
|
||||
)} but for build time only.
|
||||
-m, --meta Add metadata for the deployment (e.g.: ${chalk.dim(
|
||||
'`-m KEY=value`'
|
||||
)}). Can appear many times.
|
||||
-E ${chalk.underline('FILE')}, --dotenv=${chalk.underline(
|
||||
'FILE'
|
||||
)} Include env vars from .env file. Defaults to '.env'
|
||||
-C, --no-clipboard Do not attempt to copy URL to clipboard
|
||||
-N, --forward-npm Forward login information to install private npm modules
|
||||
--session-affinity Session affinity, \`ip\` or \`random\` (default) to control session affinity
|
||||
-S, --scope Set a custom scope
|
||||
--regions Set default regions or DCs to enable the deployment on
|
||||
--no-scale Skip scaling rules deploying with the default presets
|
||||
--no-verify Skip step of waiting until instance count meets given constraints
|
||||
|
||||
${chalk.dim(`Enforceable Types (by default, it's detected automatically):`)}
|
||||
|
||||
--npm Node.js application
|
||||
--docker Docker container
|
||||
--static Static file hosting
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Deploy the current directory
|
||||
|
||||
${chalk.cyan('$ now')}
|
||||
|
||||
${chalk.gray('–')} Deploy a custom path
|
||||
|
||||
${chalk.cyan('$ now /usr/src/project')}
|
||||
|
||||
${chalk.gray('–')} Deploy a GitHub repository
|
||||
|
||||
${chalk.cyan('$ now user/repo#ref')}
|
||||
|
||||
${chalk.gray('–')} Deploy with environment variables
|
||||
|
||||
${chalk.cyan('$ now -e NODE_ENV=production -e SECRET=@mysql-secret')}
|
||||
|
||||
${chalk.gray('–')} Show the usage information for the sub command ${chalk.dim(
|
||||
'`list`'
|
||||
)}
|
||||
|
||||
${chalk.cyan('$ now help list')}
|
||||
|
||||
`;
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
import fs from 'fs-extra';
|
||||
import { resolve, basename } from 'path';
|
||||
import { resolve, basename, parse, join } from 'path';
|
||||
import Client from '../../util/client.ts';
|
||||
import getScope from '../../util/get-scope.ts';
|
||||
import createOutput from '../../util/output';
|
||||
import code from '../../util/output/code';
|
||||
import highlight from '../../util/output/highlight';
|
||||
import param from '../../util/output/param.ts';
|
||||
import { readLocalConfig } from '../../util/config/files';
|
||||
import getArgs from '../../util/get-args';
|
||||
import { help, args } from './args';
|
||||
import * as parts from './args';
|
||||
import { handleError } from '../../util/error';
|
||||
import deploy from './deploy';
|
||||
import readPackage from '../../util/read-package';
|
||||
import preferV2Deployment, {
|
||||
hasDockerfile,
|
||||
hasServerfile,
|
||||
} from '../../util/prefer-v2-deployment';
|
||||
import getProjectName from '../../util/get-project-name';
|
||||
|
||||
export default async ctx => {
|
||||
const {
|
||||
@@ -15,11 +23,14 @@ export default async ctx => {
|
||||
config: { currentTeam },
|
||||
apiUrl,
|
||||
} = ctx;
|
||||
const combinedArgs = Object.assign({}, parts.legacyArgs, parts.latestArgs);
|
||||
|
||||
let platformVersion = null;
|
||||
let contextName = currentTeam || 'current user';
|
||||
let argv = null;
|
||||
|
||||
try {
|
||||
argv = getArgs(ctx.argv.slice(2), args);
|
||||
argv = getArgs(ctx.argv.slice(2), combinedArgs);
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
return 1;
|
||||
@@ -46,8 +57,12 @@ export default async ctx => {
|
||||
const debugEnabled = argv['--debug'];
|
||||
const output = createOutput({ debug: debugEnabled });
|
||||
const stats = {};
|
||||
const versionFlag = argv['--platform-version'];
|
||||
|
||||
if (argv['--help']) {
|
||||
const lastArg = argv._[argv._.length - 1];
|
||||
const help = lastArg === 'deploy-v1' ? parts.legacyHelp : parts.latestHelp;
|
||||
|
||||
output.print(help());
|
||||
return 2;
|
||||
}
|
||||
@@ -56,15 +71,28 @@ export default async ctx => {
|
||||
try {
|
||||
stats[path] = await fs.lstat(path);
|
||||
} catch (err) {
|
||||
output.error(
|
||||
`The specified file or directory "${basename(path)}" does not exist.`
|
||||
);
|
||||
return 1;
|
||||
const { ext } = parse(path);
|
||||
|
||||
if (versionFlag === 1 && !ext) {
|
||||
// This will ensure `-V 1 zeit/serve` (GitHub deployments) work. Since
|
||||
// GitHub repositories are never just one file, we need to set
|
||||
// the `isFile` property accordingly.
|
||||
stats[path] = {
|
||||
isFile: () => false,
|
||||
};
|
||||
} else {
|
||||
output.error(
|
||||
`The specified file or directory "${basename(path)}" does not exist.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let client = null;
|
||||
|
||||
const isFile = Object.keys(stats).length === 1 && stats[paths[0]].isFile();
|
||||
|
||||
if (authConfig && authConfig.token) {
|
||||
client = new Client({
|
||||
apiUrl,
|
||||
@@ -73,7 +101,7 @@ export default async ctx => {
|
||||
debug: debugEnabled,
|
||||
});
|
||||
try {
|
||||
({ contextName } = await getScope(client));
|
||||
({ contextName, platformVersion } = await getScope(client));
|
||||
} catch (err) {
|
||||
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
|
||||
output.error(err.message);
|
||||
@@ -84,5 +112,89 @@ export default async ctx => {
|
||||
}
|
||||
}
|
||||
|
||||
return deploy(ctx, contextName, output, stats, localConfig, args);
|
||||
const file = highlight('now.json');
|
||||
const prop = code('version');
|
||||
|
||||
if (localConfig) {
|
||||
const { version } = localConfig;
|
||||
|
||||
if (version) {
|
||||
if (typeof version === 'number') {
|
||||
if (version !== 1 && version !== 2) {
|
||||
const first = code(1);
|
||||
const second = code(2);
|
||||
|
||||
output.error(
|
||||
`The value of the ${prop} property within ${file} can only be ${first} or ${second}.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
platformVersion = version;
|
||||
} else {
|
||||
output.error(
|
||||
`The ${prop} property inside your ${file} file must be a number.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (versionFlag) {
|
||||
if (versionFlag !== 1 && versionFlag !== 2) {
|
||||
output.error(
|
||||
`The ${param('--platform-version')} option must be either ${code(
|
||||
'1'
|
||||
)} or ${code('2')}.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
platformVersion = versionFlag;
|
||||
}
|
||||
|
||||
if (
|
||||
platformVersion === 1 &&
|
||||
versionFlag !== 1 &&
|
||||
!argv['--docker'] &&
|
||||
!argv['--npm']
|
||||
) {
|
||||
// Only check when it was not set via CLI flag
|
||||
const reason = await preferV2Deployment({
|
||||
client,
|
||||
localConfig,
|
||||
projectName: getProjectName({
|
||||
argv,
|
||||
nowConfig: localConfig || {},
|
||||
isFile,
|
||||
paths,
|
||||
}),
|
||||
hasServerfile: await hasServerfile(paths[0]),
|
||||
hasDockerfile: await hasDockerfile(paths[0]),
|
||||
pkg: await readPackage(join(paths[0], 'package.json')),
|
||||
});
|
||||
|
||||
if (reason) {
|
||||
output.note(reason);
|
||||
platformVersion = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (platformVersion === null || platformVersion > 1) {
|
||||
return require('./latest').default(
|
||||
ctx,
|
||||
contextName,
|
||||
output,
|
||||
stats,
|
||||
localConfig,
|
||||
parts.latestArgs
|
||||
);
|
||||
}
|
||||
|
||||
return require('./legacy').default(
|
||||
ctx,
|
||||
contextName,
|
||||
output,
|
||||
parts.legacyArgsMri
|
||||
);
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ import Client from '../../util/client';
|
||||
import { handleError } from '../../util/error';
|
||||
import getArgs from '../../util/get-args';
|
||||
import toHumanPath from '../../util/humanize-path';
|
||||
import Now from '../../util/now';
|
||||
import Now from '../../util';
|
||||
import stamp from '../../util/output/stamp.ts';
|
||||
import createDeploy from '../../util/deploy/create-deploy';
|
||||
import getDeploymentByIdOrHost from '../../util/deploy/get-deployment-by-id-or-host';
|
||||
@@ -35,7 +35,7 @@ import {
|
||||
ConflictingPathSegment,
|
||||
BuildError,
|
||||
NotDomainOwner,
|
||||
} from '../../util/errors';
|
||||
} from '../../util/errors-ts';
|
||||
import { SchemaValidationFailed } from '../../util/errors';
|
||||
import purchaseDomainIfAvailable from '../../util/domains/purchase-domain-if-available';
|
||||
import isWildcardAlias from '../../util/alias/is-wildcard-alias';
|
||||
@@ -45,7 +45,7 @@ import {
|
||||
getLinkedProject,
|
||||
linkFolderToProject,
|
||||
} from '../../util/projects/link';
|
||||
import { getProjectName } from '../../util/get-project-name';
|
||||
import getProjectName from '../../util/get-project-name';
|
||||
import selectOrg from '../../util/input/select-org';
|
||||
import inputProject from '../../util/input/input-project';
|
||||
import { prependEmoji, emoji } from '../../util/emoji';
|
||||
@@ -225,6 +225,7 @@ export default async function main(
|
||||
const paths = Object.keys(stats);
|
||||
const debugEnabled = argv['--debug'];
|
||||
|
||||
// $FlowFixMe
|
||||
const isTTY = process.stdout.isTTY;
|
||||
const quiet = !isTTY;
|
||||
|
||||
1139
packages/now-cli/src/commands/deploy/legacy.ts
Normal file
1139
packages/now-cli/src/commands/deploy/legacy.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,7 @@ const help = () => {
|
||||
-h, --help Output usage information
|
||||
-d, --debug Debug mode [off]
|
||||
-l, --listen [uri] Specify a URI endpoint on which to listen [0.0.0.0:3000]
|
||||
-t, --token [token] Specify an Authorization Token
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
DNSPermissionDenied,
|
||||
DNSInvalidPort,
|
||||
DNSInvalidType,
|
||||
} from '../../util/errors';
|
||||
} from '../../util/errors-ts';
|
||||
import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import addDNSRecord from '../../util/dns/add-dns-record';
|
||||
|
||||
@@ -3,7 +3,7 @@ import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import Client from '../../util/client';
|
||||
import getScope from '../../util/get-scope';
|
||||
import { DomainNotFound, InvalidDomain } from '../../util/errors';
|
||||
import { DomainNotFound, InvalidDomain } from '../../util/errors-ts';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import importZonefile from '../../util/dns/import-zonefile';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import plural from 'pluralize';
|
||||
import { Output } from '../../util/output';
|
||||
import { DomainNotFound } from '../../util/errors';
|
||||
import { DomainNotFound } from '../../util/errors-ts';
|
||||
import { ThenArg, DNSRecord, NowContext } from '../../types';
|
||||
import Client from '../../util/client';
|
||||
import formatTable from '../../util/format-table';
|
||||
|
||||
@@ -3,7 +3,7 @@ import psl from 'psl';
|
||||
|
||||
import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import * as ERRORS from '../../util/errors';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import addDomain from '../../util/domains/add-domain';
|
||||
import Client from '../../util/client';
|
||||
import cmd from '../../util/output/cmd';
|
||||
|
||||
@@ -3,7 +3,7 @@ import psl from 'psl';
|
||||
|
||||
import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import * as ERRORS from '../../util/errors';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import Client from '../../util/client';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import getDomainPrice from '../../util/domains/get-domain-price';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import chalk from 'chalk';
|
||||
import { DomainNotFound, DomainPermissionDenied } from '../../util/errors';
|
||||
import { DomainNotFound, DomainPermissionDenied } from '../../util/errors-ts';
|
||||
import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import Client from '../../util/client';
|
||||
|
||||
@@ -3,7 +3,7 @@ import plural from 'pluralize';
|
||||
|
||||
import { NowContext, User, Team } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import * as ERRORS from '../../util/errors';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import Client from '../../util/client';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import getScope from '../../util/get-scope';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import chalk from 'chalk';
|
||||
import plural from 'pluralize';
|
||||
|
||||
import { DomainNotFound, DomainPermissionDenied } from '../../util/errors';
|
||||
import { DomainNotFound, DomainPermissionDenied } from '../../util/errors-ts';
|
||||
import { NowContext, Domain } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import Client from '../../util/client';
|
||||
@@ -12,7 +12,7 @@ import getScope from '../../util/get-scope';
|
||||
import removeAliasById from '../../util/alias/remove-alias-by-id';
|
||||
import removeDomainByName from '../../util/domains/remove-domain-by-name';
|
||||
import stamp from '../../util/output/stamp';
|
||||
import * as ERRORS from '../../util/errors';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import param from '../../util/output/param';
|
||||
import promptBool from '../../util/input/prompt-bool';
|
||||
import setCustomSuffix from '../../util/domains/set-custom-suffix';
|
||||
|
||||
@@ -2,7 +2,7 @@ import chalk from 'chalk';
|
||||
|
||||
import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import * as ERRORS from '../../util/errors';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import Client from '../../util/client';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import getScope from '../../util/get-scope';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import chalk from 'chalk';
|
||||
import { NowContext } from '../../types';
|
||||
import { Output } from '../../util/output';
|
||||
import * as ERRORS from '../../util/errors';
|
||||
import * as ERRORS from '../../util/errors-ts';
|
||||
import Client from '../../util/client';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import formatDnsTable from '../../util/format-dns-table';
|
||||
|
||||
@@ -6,11 +6,14 @@ export default new Map([
|
||||
['cert', 'certs'],
|
||||
['certs', 'certs'],
|
||||
['deploy', 'deploy'],
|
||||
['deploy-v1', 'deploy'],
|
||||
['deploy-v2', 'deploy'],
|
||||
['dev', 'dev'],
|
||||
['dns', 'dns'],
|
||||
['domain', 'domains'],
|
||||
['domains', 'domains'],
|
||||
['downgrade', 'upgrade'],
|
||||
['help', 'help'],
|
||||
['init', 'init'],
|
||||
['inspect', 'inspect'],
|
||||
['list', 'list'],
|
||||
@@ -24,6 +27,7 @@ export default new Map([
|
||||
['projects', 'projects'],
|
||||
['remove', 'remove'],
|
||||
['rm', 'remove'],
|
||||
['scale', 'scale'],
|
||||
['secret', 'secrets'],
|
||||
['secrets', 'secrets'],
|
||||
['switch', 'teams'],
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import chalk from 'chalk';
|
||||
import table from 'text-table';
|
||||
import getArgs from '../util/get-args';
|
||||
import buildsList from '../util/output/builds';
|
||||
import routesList from '../util/output/routes';
|
||||
import indent from '../util/output/indent';
|
||||
import cmd from '../util/output/cmd.ts';
|
||||
import createOutput from '../util/output';
|
||||
import Now from '../util/now';
|
||||
import Now from '../util';
|
||||
import logo from '../util/output/logo';
|
||||
import elapsed from '../util/output/elapsed.ts';
|
||||
import { handleError } from '../util/error';
|
||||
import strlen from '../util/strlen.ts';
|
||||
import Client from '../util/client.ts';
|
||||
import getScope from '../util/get-scope.ts';
|
||||
|
||||
const STATIC = 'STATIC';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
${chalk.bold(`${logo} now inspect`)} <url>
|
||||
@@ -44,6 +48,7 @@ const help = () => {
|
||||
};
|
||||
|
||||
export default async function main(ctx) {
|
||||
let id;
|
||||
let deployment;
|
||||
let argv;
|
||||
|
||||
@@ -65,7 +70,7 @@ export default async function main(ctx) {
|
||||
const { print, log, error } = output;
|
||||
|
||||
// extract the first parameter
|
||||
const [, deploymentIdOrHost] = argv._;
|
||||
id = argv._[1];
|
||||
|
||||
if (argv._.length !== 2) {
|
||||
error(`${cmd('now inspect <url>')} expects exactly one argument`);
|
||||
@@ -102,24 +107,20 @@ export default async function main(ctx) {
|
||||
// resolve the deployment, since we might have been given an alias
|
||||
const depFetchStart = Date.now();
|
||||
const cancelWait = output.spinner(
|
||||
`Fetching deployment "${deploymentIdOrHost}" in ${chalk.bold(contextName)}`
|
||||
`Fetching deployment "${id}" in ${chalk.bold(contextName)}`
|
||||
);
|
||||
|
||||
try {
|
||||
deployment = await now.findDeployment(deploymentIdOrHost);
|
||||
deployment = await now.findDeployment(id);
|
||||
} catch (err) {
|
||||
cancelWait();
|
||||
if (err.status === 404) {
|
||||
error(
|
||||
`Failed to find deployment "${deploymentIdOrHost}" in ${chalk.bold(
|
||||
contextName
|
||||
)}`
|
||||
);
|
||||
error(`Failed to find deployment "${id}" in ${chalk.bold(contextName)}`);
|
||||
return 1;
|
||||
}
|
||||
if (err.status === 403) {
|
||||
error(
|
||||
`No permission to access deployment "${deploymentIdOrHost}" in ${chalk.bold(
|
||||
`No permission to access deployment "${id}" in ${chalk.bold(
|
||||
contextName
|
||||
)}`
|
||||
);
|
||||
@@ -129,12 +130,39 @@ export default async function main(ctx) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const { id, name, url, created, routes, readyState } = deployment;
|
||||
const {
|
||||
id: finalId,
|
||||
name,
|
||||
state,
|
||||
type,
|
||||
slot,
|
||||
sessionAffinity,
|
||||
url,
|
||||
created,
|
||||
limits,
|
||||
version,
|
||||
routes,
|
||||
readyState,
|
||||
} = deployment;
|
||||
|
||||
const { builds } =
|
||||
deployment.version === 2
|
||||
? await now.fetch(`/v1/now/deployments/${id}/builds`)
|
||||
: { builds: [] };
|
||||
const isBuilds = version === 2;
|
||||
const buildsUrl = `/v1/now/deployments/${finalId}/builds`;
|
||||
|
||||
const [scale, events, { builds }] = await Promise.all([
|
||||
caught(
|
||||
now.fetch(`/v3/now/deployments/${encodeURIComponent(finalId)}/instances`)
|
||||
),
|
||||
type === STATIC
|
||||
? null
|
||||
: caught(
|
||||
now.fetch(
|
||||
`/v1/now/deployments/${encodeURIComponent(
|
||||
finalId
|
||||
)}/events?types=event`
|
||||
)
|
||||
),
|
||||
isBuilds ? now.fetch(buildsUrl) : { builds: [] },
|
||||
]);
|
||||
|
||||
cancelWait();
|
||||
log(
|
||||
@@ -145,9 +173,21 @@ export default async function main(ctx) {
|
||||
|
||||
print('\n');
|
||||
print(chalk.bold(' General\n\n'));
|
||||
print(` ${chalk.cyan('id')}\t\t${id}\n`);
|
||||
print(` ${chalk.cyan('version')}\t${version}\n`);
|
||||
print(` ${chalk.cyan('id')}\t\t${finalId}\n`);
|
||||
print(` ${chalk.cyan('name')}\t${name}\n`);
|
||||
print(` ${chalk.cyan('readyState')}\t${stateString(readyState)}\n`);
|
||||
print(
|
||||
` ${chalk.cyan('readyState')}\t${stateString(state || readyState)}\n`
|
||||
);
|
||||
if (!isBuilds) {
|
||||
print(` ${chalk.cyan('type')}\t${type}\n`);
|
||||
}
|
||||
if (slot) {
|
||||
print(` ${chalk.cyan('slot')}\t${slot}\n`);
|
||||
}
|
||||
if (sessionAffinity) {
|
||||
print(` ${chalk.cyan('affinity')}\t${sessionAffinity}\n`);
|
||||
}
|
||||
print(` ${chalk.cyan('url')}\t\t${url}\n`);
|
||||
if (created) {
|
||||
print(
|
||||
@@ -178,7 +218,95 @@ export default async function main(ctx) {
|
||||
print(`\n\n`);
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (limits) {
|
||||
print(chalk.bold(' Limits\n\n'));
|
||||
print(
|
||||
` ${chalk.dim('duration')}\t\t${limits.duration} ${elapsed(
|
||||
limits.duration
|
||||
)}\n`
|
||||
);
|
||||
print(
|
||||
` ${chalk.dim('maxConcurrentReqs')}\t${limits.maxConcurrentReqs}\n`
|
||||
);
|
||||
print(
|
||||
` ${chalk.dim('timeout')}\t\t${limits.timeout} ${elapsed(
|
||||
limits.timeout
|
||||
)}\n`
|
||||
);
|
||||
print('\n');
|
||||
}
|
||||
|
||||
if (type === STATIC || isBuilds) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
print(chalk.bold(' Scale\n\n'));
|
||||
|
||||
let exitCode = 0;
|
||||
|
||||
if (scale instanceof Error) {
|
||||
error(`Scale information unavailable: ${scale}`);
|
||||
exitCode = 1;
|
||||
} else {
|
||||
const dcs = Object.keys(scale);
|
||||
const t = [['dc', 'min', 'max', 'current'].map(v => chalk.gray(v))];
|
||||
for (const dc of dcs) {
|
||||
const { instances } = scale[dc];
|
||||
const cfg = deployment.scale[dc] || {};
|
||||
t.push([dc, cfg.min || 0, cfg.max || 0, instances.length]);
|
||||
}
|
||||
print(
|
||||
`${table(t, {
|
||||
align: ['l', 'c', 'c', 'c'],
|
||||
hsep: ' '.repeat(8),
|
||||
stringLength: strlen,
|
||||
}).replace(/^(.*)/gm, ' $1')}\n`
|
||||
);
|
||||
print('\n');
|
||||
}
|
||||
|
||||
print(chalk.bold(' Events\n\n'));
|
||||
if (events instanceof Error) {
|
||||
error(`Events unavailable: ${scale}`);
|
||||
exitCode = 1;
|
||||
} else if (events) {
|
||||
events.forEach(data => {
|
||||
if (!data.event) return; // keepalive
|
||||
print(
|
||||
` ${chalk.gray(new Date(data.created).toISOString())} ${
|
||||
data.event
|
||||
} ${getEventMetadata(data)}\n`
|
||||
);
|
||||
});
|
||||
print('\n');
|
||||
}
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
// gets the metadata that should be printed next to
|
||||
// each event
|
||||
|
||||
function getEventMetadata({ event, payload }) {
|
||||
if (event === 'state') {
|
||||
return chalk.bold(payload.value);
|
||||
}
|
||||
|
||||
if (event === 'instance-start' || event === 'instance-stop') {
|
||||
if (payload.dc != null) {
|
||||
return chalk.green(`(${payload.dc})`);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
// makes sure the promise never rejects, exposing the error
|
||||
// as the resolved value instead
|
||||
function caught(p) {
|
||||
return new Promise(r => {
|
||||
p.then(r).catch(r);
|
||||
});
|
||||
}
|
||||
|
||||
// renders the state string
|
||||
@@ -190,9 +318,6 @@ function stateString(s) {
|
||||
case 'ERROR':
|
||||
return chalk.red(s);
|
||||
|
||||
case 'BUILDING':
|
||||
return chalk.grey(s);
|
||||
|
||||
case 'READY':
|
||||
return s;
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import plural from 'pluralize';
|
||||
import table from 'text-table';
|
||||
import Now from '../util/now';
|
||||
import Now from '../util';
|
||||
import getAliases from '../util/alias/get-aliases';
|
||||
import getArgs from '../util/get-args';
|
||||
import getDeploymentInstances from '../util/deploy/get-deployment-instances';
|
||||
import createOutput from '../util/output';
|
||||
import { handleError } from '../util/error';
|
||||
import cmd from '../util/output/cmd.ts';
|
||||
@@ -13,7 +14,7 @@ import wait from '../util/output/wait';
|
||||
import strlen from '../util/strlen.ts';
|
||||
import Client from '../util/client.ts';
|
||||
import getScope from '../util/get-scope.ts';
|
||||
import { toHost } from '../util/to-host';
|
||||
import toHost from '../util/to-host';
|
||||
import parseMeta from '../util/parse-meta';
|
||||
import { isValidName } from '../util/is-valid-name';
|
||||
|
||||
@@ -39,6 +40,7 @@ const help = () => {
|
||||
-m, --meta Filter deployments by metadata (e.g.: ${chalk.dim(
|
||||
'`-m KEY=value`'
|
||||
)}). Can appear many times.
|
||||
-N, --next Show next page of results
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
@@ -59,9 +61,17 @@ const help = () => {
|
||||
${chalk.gray('–')} Filter deployments by metadata
|
||||
|
||||
${chalk.cyan('$ now ls -m key1=value1 -m key2=value2')}
|
||||
|
||||
${chalk.gray('–')} Paginate deployments for a project, where ${chalk.dim(
|
||||
'`1584722256178`'
|
||||
)} is the time in milliseconds since the UNIX epoch.
|
||||
|
||||
${chalk.cyan(`$ now ls my-app --next 1584722256178`)}
|
||||
`);
|
||||
};
|
||||
|
||||
// Options
|
||||
// $FlowFixMe
|
||||
export default async function main(ctx) {
|
||||
let argv;
|
||||
|
||||
@@ -71,6 +81,8 @@ export default async function main(ctx) {
|
||||
'--meta': [String],
|
||||
'-a': '--all',
|
||||
'-m': '--meta',
|
||||
'--next': Number,
|
||||
'-N': '--next',
|
||||
});
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
@@ -123,6 +135,13 @@ export default async function main(ctx) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const nextTimestamp = argv['--next'];
|
||||
|
||||
if (typeof nextTimestamp !== undefined && Number.isNaN(nextTimestamp)) {
|
||||
error('Please provide a number for flag `--next`');
|
||||
return 1;
|
||||
}
|
||||
|
||||
const stopSpinner = wait(
|
||||
`Fetching deployments in ${chalk.bold(contextName)}`
|
||||
);
|
||||
@@ -160,16 +179,22 @@ export default async function main(ctx) {
|
||||
host = asHost;
|
||||
}
|
||||
|
||||
let deployments;
|
||||
let response;
|
||||
|
||||
try {
|
||||
debug('Fetching deployments');
|
||||
deployments = await now.list(app, { version: 5, meta });
|
||||
response = await now.list(app, {
|
||||
version: 6,
|
||||
meta,
|
||||
nextTimestamp,
|
||||
});
|
||||
} catch (err) {
|
||||
stopSpinner();
|
||||
throw err;
|
||||
}
|
||||
|
||||
let { deployments, pagination } = response;
|
||||
|
||||
if (app && !deployments.length) {
|
||||
debug(
|
||||
'No deployments: attempting to find deployment that matches supplied app name'
|
||||
@@ -193,19 +218,60 @@ export default async function main(ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
if (app && !deployments.length) {
|
||||
debug(
|
||||
'No deployments: attempting to find aliases that matches supplied app name'
|
||||
);
|
||||
const aliases = await getAliases(now);
|
||||
const item = aliases.find(e => e.uid === app || e.alias === app);
|
||||
|
||||
if (item) {
|
||||
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 instances = await getDeploymentInstances(
|
||||
now,
|
||||
item.deploymentId,
|
||||
'now_cli_alias_instances'
|
||||
);
|
||||
match.instanceCount = Object.keys(instances).reduce(
|
||||
(count, dc) => count + instances[dc].instances.length,
|
||||
0
|
||||
);
|
||||
if (match !== null && typeof match !== 'undefined') {
|
||||
deployments = Array.of(match);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
now.close();
|
||||
|
||||
if (argv['--all']) {
|
||||
await Promise.all(
|
||||
deployments.map(async ({ uid, instanceCount }, i) => {
|
||||
deployments[i].instances =
|
||||
instanceCount > 0 ? await now.listInstances(uid) : [];
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (host) {
|
||||
deployments = deployments.filter(deployment => deployment.url === host);
|
||||
}
|
||||
|
||||
stopSpinner();
|
||||
log(
|
||||
`Fetched ${plural(
|
||||
'deployment',
|
||||
deployments.length,
|
||||
true
|
||||
)} under ${chalk.bold(contextName)} ${elapsed(Date.now() - start)}`
|
||||
`Deployments under ${chalk.bold(contextName)} ${elapsed(
|
||||
Date.now() - start
|
||||
)}`
|
||||
);
|
||||
|
||||
// we don't output the table headers if we have no deployments
|
||||
@@ -218,6 +284,8 @@ export default async function main(ctx) {
|
||||
log(
|
||||
`To list more deployments for a project run ${cmd('now ls [project]')}`
|
||||
);
|
||||
} else if (!argv['--all']) {
|
||||
log(`To list deployment instances run ${cmd('now ls --all [project]')}`);
|
||||
}
|
||||
|
||||
print('\n');
|
||||
@@ -235,9 +303,18 @@ export default async function main(ctx) {
|
||||
getProjectName(dep),
|
||||
chalk.bold((includeScheme ? 'https://' : '') + dep.url),
|
||||
stateString(dep.state),
|
||||
chalk.gray(ms(Date.now() - new Date(dep.created))),
|
||||
chalk.gray(ms(Date.now() - new Date(dep.createdAt))),
|
||||
dep.creator.username,
|
||||
],
|
||||
...(argv['--all']
|
||||
? dep.instances.map(i => [
|
||||
'',
|
||||
` ${chalk.gray('-')} ${i.url} `,
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
])
|
||||
: []),
|
||||
])
|
||||
// flatten since the previous step returns a nested
|
||||
// array of the deployment and (optionally) its instances
|
||||
@@ -257,6 +334,10 @@ export default async function main(ctx) {
|
||||
}
|
||||
).replace(/^/gm, ' ')}\n\n`
|
||||
);
|
||||
|
||||
if (pagination && deployments.length === 20) {
|
||||
log(`To display the next page use the flag --next ${pagination.next}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getProjectName(d) {
|
||||
@@ -288,7 +369,7 @@ function stateString(s) {
|
||||
// sorts by most recent deployment
|
||||
function sortRecent() {
|
||||
return function recencySort(a, b) {
|
||||
return b.created - a.created;
|
||||
return b.createdAt - a.createdAt;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import mri from 'mri';
|
||||
import chalk from 'chalk';
|
||||
import fetch from 'node-fetch';
|
||||
import ora from 'ora';
|
||||
import logo from '../util/output/logo';
|
||||
// @ts-ignore
|
||||
import { handleError } from '../util/error';
|
||||
import {
|
||||
readConfigFile,
|
||||
writeToConfigFile,
|
||||
readAuthConfigFile,
|
||||
writeToAuthConfigFile
|
||||
writeToAuthConfigFile,
|
||||
} from '../util/config/files';
|
||||
import error from '../util/output/error';
|
||||
import exit from '../util/exit';
|
||||
import getArgs from '../util/get-args';
|
||||
import { NowContext } from '../types';
|
||||
import createOutput, { Output } from '../util/output';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
@@ -35,56 +35,31 @@ const help = () => {
|
||||
`);
|
||||
};
|
||||
|
||||
let argv;
|
||||
let apiUrl;
|
||||
export default async function main(ctx: NowContext): Promise<number> {
|
||||
let argv;
|
||||
|
||||
const main = async ctx => {
|
||||
argv = mri(ctx.argv.slice(2), {
|
||||
boolean: ['help'],
|
||||
alias: {
|
||||
help: 'h'
|
||||
}
|
||||
});
|
||||
|
||||
apiUrl = ctx.apiUrl;
|
||||
argv._ = argv._.slice(1);
|
||||
|
||||
if (argv.help || argv._[0] === 'help') {
|
||||
help();
|
||||
await exit(0);
|
||||
}
|
||||
|
||||
logout();
|
||||
};
|
||||
|
||||
export default async ctx => {
|
||||
try {
|
||||
await main(ctx);
|
||||
argv = getArgs(ctx.argv.slice(2), {
|
||||
'--help': Boolean,
|
||||
'-h': '--help',
|
||||
});
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
process.exit(1);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
const revokeToken = async (token) => {
|
||||
const options = {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
};
|
||||
|
||||
const result = await fetch(`${apiUrl}/v3/user/tokens/current`, options);
|
||||
|
||||
if (!result.ok) {
|
||||
console.error(error('Not able to log out'));
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
|
||||
const logout = async () => {
|
||||
const spinner = ora({
|
||||
text: 'Logging out...'
|
||||
}).start();
|
||||
const debugEnabled = argv['--debug'];
|
||||
const output = createOutput({ debug: debugEnabled });
|
||||
return logout(ctx.apiUrl, output);
|
||||
}
|
||||
|
||||
const logout = async (apiUrl: string, output: Output) => {
|
||||
const spinner = output.spinner('Logging out...', 200);
|
||||
|
||||
const configContent = readConfigFile();
|
||||
const authContent = readAuthConfigFile();
|
||||
@@ -103,19 +78,33 @@ const logout = async () => {
|
||||
delete authContent.token;
|
||||
|
||||
try {
|
||||
await writeToConfigFile(configContent);
|
||||
await writeToAuthConfigFile(authContent);
|
||||
writeToConfigFile(configContent);
|
||||
writeToAuthConfigFile(authContent);
|
||||
output.debug('Configuration has been deleted');
|
||||
} catch (err) {
|
||||
spinner.fail(`Couldn't remove config while logging out`);
|
||||
process.exit(1);
|
||||
spinner();
|
||||
output.error(`Couldn't remove config while logging out`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
try {
|
||||
await revokeToken(token);
|
||||
} catch (err) {
|
||||
spinner.fail('Could not revoke token on logout');
|
||||
process.exit(1);
|
||||
const res = await fetch(`${apiUrl}/v3/user/tokens/current`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status === 403) {
|
||||
output.debug('Token is invalid so it cannot be revoked');
|
||||
} else if (res.status !== 200) {
|
||||
spinner();
|
||||
const err = await res.json();
|
||||
output.error('Failed to revoke token');
|
||||
output.debug(err ? err.message : '');
|
||||
return 1;
|
||||
}
|
||||
|
||||
spinner.succeed('Logged out!');
|
||||
spinner();
|
||||
output.log('Logged out!');
|
||||
return 0;
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import mri from 'mri';
|
||||
import chalk from 'chalk';
|
||||
import Now from '../util/now';
|
||||
import Now from '../util';
|
||||
import createOutput from '../util/output';
|
||||
import logo from '../util/output/logo';
|
||||
import elapsed from '../util/output/elapsed.ts';
|
||||
|
||||
@@ -3,7 +3,7 @@ import chalk from 'chalk';
|
||||
import ms from 'ms';
|
||||
import plural from 'pluralize';
|
||||
import table from 'text-table';
|
||||
import Now from '../util/now';
|
||||
import Now from '../util';
|
||||
import getAliases from '../util/alias/get-aliases';
|
||||
import createOutput from '../util/output';
|
||||
import logo from '../util/output/logo';
|
||||
|
||||
340
packages/now-cli/src/commands/scale.js
Normal file
340
packages/now-cli/src/commands/scale.js
Normal file
@@ -0,0 +1,340 @@
|
||||
import ms from 'ms';
|
||||
import chalk from 'chalk';
|
||||
|
||||
import cmd from '../util/output/cmd.ts';
|
||||
import createOutput from '../util/output';
|
||||
import logo from '../util/output/logo';
|
||||
import stamp from '../util/output/stamp.ts';
|
||||
|
||||
import Now from '../util';
|
||||
import getArgs from '../util/get-args';
|
||||
import Client from '../util/client.ts';
|
||||
import getScope from '../util/get-scope.ts';
|
||||
import getDCsFromArgs from '../util/scale/get-dcs-from-args';
|
||||
import getDeploymentByIdOrHost from '../util/deploy/get-deployment-by-id-or-host';
|
||||
import getDeploymentByIdOrThrow from '../util/deploy/get-deployment-by-id-or-throw';
|
||||
import getMaxFromArgs from '../util/scale/get-max-from-args';
|
||||
import getMinFromArgs from '../util/scale/get-min-from-args';
|
||||
import patchDeploymentScale from '../util/scale/patch-deployment-scale';
|
||||
import waitVerifyDeploymentScale from '../util/scale/wait-verify-deployment-scale';
|
||||
import { handleError } from '../util/error';
|
||||
import {
|
||||
VerifyScaleTimeout,
|
||||
DeploymentTypeUnsupported,
|
||||
} from '../util/errors-ts';
|
||||
import {
|
||||
DeploymentNotFound,
|
||||
DeploymentPermissionDenied,
|
||||
ForbiddenScaleMaxInstances,
|
||||
ForbiddenScaleMinInstances,
|
||||
InvalidArgsForMinMaxScale,
|
||||
InvalidMaxForScale,
|
||||
InvalidMinForScale,
|
||||
InvalidScaleMinMaxRelation,
|
||||
NotSupportedMinScaleSlots,
|
||||
} from '../util/errors-ts';
|
||||
import { InvalidAllForScale, InvalidRegionOrDCForScale } from '../util/errors';
|
||||
import handleCertError from '../util/certs/handle-cert-error';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
${chalk.bold(`${logo} now scale`)} <url> <dc> [min] [max]
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`now.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.now`'} directory
|
||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
-d, --debug Debug mode [off]
|
||||
-S, --scope Set a custom scope
|
||||
-n, --no-verify Skip step of waiting until instance count meets given constraints
|
||||
-t, --verify-timeout How long to wait for verification to complete [5m]
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray(
|
||||
'–'
|
||||
)} Enable your deployment in all datacenters (min: 0, max: auto)
|
||||
|
||||
${chalk.cyan('$ now scale my-deployment-123.now.sh all')}
|
||||
|
||||
${chalk.gray(
|
||||
'-'
|
||||
)} Enable your deployment in the SFO datacenter (min: 0, max: auto)
|
||||
|
||||
${chalk.cyan('$ now scale my-deployment-123.now.sh sfo')}
|
||||
|
||||
${chalk.gray(
|
||||
'–'
|
||||
)} Scale a deployment in all datacenters to 3 instances at all times (no sleep)
|
||||
|
||||
${chalk.cyan('$ now scale my-deployment-123.now.sh all 3')}
|
||||
|
||||
${chalk.gray(
|
||||
'–'
|
||||
)} Enable your deployment in all datacenters, with auto-scaling
|
||||
|
||||
${chalk.cyan('$ now scale my-deployment-123.now.sh all auto')}
|
||||
`);
|
||||
};
|
||||
|
||||
export default async function main(ctx) {
|
||||
let argv;
|
||||
|
||||
try {
|
||||
argv = getArgs(ctx.argv.slice(2), {
|
||||
'--verify-timeout': Number,
|
||||
'--no-verify': Boolean,
|
||||
'-n': '--no-verify',
|
||||
});
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argv['--help']) {
|
||||
help();
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Prepare the context
|
||||
const {
|
||||
authConfig: { token },
|
||||
config,
|
||||
} = ctx;
|
||||
const { currentTeam } = config;
|
||||
const { apiUrl } = ctx;
|
||||
const debug = argv['--debug'];
|
||||
|
||||
// $FlowFixMe
|
||||
const now = new Now({ apiUrl, token, debug, currentTeam });
|
||||
const output = createOutput({ debug });
|
||||
const client = new Client({ apiUrl, token, currentTeam, debug });
|
||||
let contextName = null;
|
||||
|
||||
try {
|
||||
({ contextName } = await getScope(client));
|
||||
} catch (err) {
|
||||
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
|
||||
output.error(err.message);
|
||||
return 1;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Fail if the user is providing an old command
|
||||
if (argv._[1] === 'ls') {
|
||||
output.error(
|
||||
`${cmd('now scale ls')} has been deprecated. Use ${cmd(
|
||||
'now ls'
|
||||
)} and ${cmd('now inspect <url>')}`
|
||||
);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Ensure the number of arguments is between the allower range
|
||||
if (argv._.length < 3 || argv._.length > 5) {
|
||||
output.error(
|
||||
`${cmd(
|
||||
'now scale <url> <dc> [min] [max]'
|
||||
)} expects at least two arguments`
|
||||
);
|
||||
help();
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
const dcs = getDCsFromArgs(argv._);
|
||||
if (dcs instanceof InvalidAllForScale) {
|
||||
output.error(
|
||||
'The region value "all" was used, but it cannot be used alongside other region or dc identifiers'
|
||||
);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
if (dcs instanceof InvalidRegionOrDCForScale) {
|
||||
output.error(
|
||||
`The value "${dcs.meta.regionOrDC}" is not a valid region or DC identifier`
|
||||
);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
const min = getMinFromArgs(argv._);
|
||||
if (min instanceof InvalidMinForScale) {
|
||||
output.error(
|
||||
`Invalid <min> parameter "${min.meta.value}". A number or "auto" were expected`
|
||||
);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
const max = getMaxFromArgs(argv._);
|
||||
if (max instanceof InvalidMinForScale) {
|
||||
output.error(
|
||||
`Invalid <min> parameter "${max.meta.value}". A number or "auto" were expected`
|
||||
);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
if (max instanceof InvalidArgsForMinMaxScale) {
|
||||
output.error(
|
||||
`Invalid number of arguments: expected <min> ("${max.meta.min}") and [max]`
|
||||
);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
if (max instanceof InvalidMaxForScale) {
|
||||
output.error(
|
||||
`Invalid <max> parameter "${max.meta.value}". A number or "auto" were expected`
|
||||
);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Fetch the deployment
|
||||
const deploymentStamp = stamp();
|
||||
const deployment = handleCertError(
|
||||
output,
|
||||
await getDeploymentByIdOrHost(now, contextName, argv._[1])
|
||||
);
|
||||
|
||||
if (deployment === 1) {
|
||||
return deployment;
|
||||
}
|
||||
if (deployment instanceof DeploymentPermissionDenied) {
|
||||
output.error(
|
||||
`No permission to access deployment ${chalk.dim(
|
||||
deployment.meta.id
|
||||
)} under ${chalk.bold(deployment.meta.context)}`
|
||||
);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
if (deployment instanceof DeploymentNotFound) {
|
||||
output.error(
|
||||
`Failed to find deployment "${argv._[1]}" in ${chalk.bold(contextName)}`
|
||||
);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
output.log(`Fetched deployment "${deployment.url}" ${deploymentStamp()}`);
|
||||
|
||||
// Make sure the deployment can be scaled
|
||||
if (deployment.type === 'STATIC') {
|
||||
output.error('Scaling rules cannot be set on static deployments');
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
if (deployment.state === 'ERROR') {
|
||||
output.error('Cannot scale a deployment in the ERROR state');
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
if (deployment.version === 2) {
|
||||
output.error('Cannot scale a Now 2.0 deployment');
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
const scaleArgs = dcs.reduce(
|
||||
(result, dc) => ({ ...result, [dc]: { min, max } }),
|
||||
{}
|
||||
);
|
||||
output.debug(
|
||||
`Setting scale deployment presets to ${JSON.stringify(scaleArgs)}`
|
||||
);
|
||||
|
||||
// Set the deployment scale
|
||||
const scaleStamp = stamp();
|
||||
const result = await patchDeploymentScale(
|
||||
output,
|
||||
now,
|
||||
deployment.uid,
|
||||
scaleArgs,
|
||||
deployment.url
|
||||
);
|
||||
if (result instanceof ForbiddenScaleMinInstances) {
|
||||
output.error(
|
||||
`You can't scale to more than ${result.meta.max} min instances with your current plan.`
|
||||
);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
if (result instanceof ForbiddenScaleMaxInstances) {
|
||||
output.error(
|
||||
`You can't scale to more than ${result.meta.max} max instances with your current plan.`
|
||||
);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
if (result instanceof InvalidScaleMinMaxRelation) {
|
||||
output.error(`Min number of instances can't be higher than max.`);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
if (result instanceof NotSupportedMinScaleSlots) {
|
||||
output.error(
|
||||
`Cloud v2 does not yet support setting a non-zero min number of instances.`
|
||||
);
|
||||
output.log('Read more: https://err.sh/now/v2-no-min');
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
if (result instanceof DeploymentTypeUnsupported) {
|
||||
output.error(`This region only accepts Serverless Docker Deployments.`);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`${chalk.gray('>')} Scale rules for ${dcs
|
||||
.map(d => chalk.bold(d))
|
||||
.join(', ')} (min: ${chalk.bold(min)}, max: ${chalk.bold(
|
||||
max
|
||||
)}) saved ${scaleStamp()}`
|
||||
);
|
||||
|
||||
if (argv['--no-verify']) {
|
||||
now.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Verify that the scale presets are there
|
||||
const verifyStamp = stamp();
|
||||
const updatedDeployment = await getDeploymentByIdOrThrow(
|
||||
now,
|
||||
contextName,
|
||||
deployment.uid
|
||||
);
|
||||
if (updatedDeployment.type === 'NPM' || updatedDeployment.type === 'DOCKER') {
|
||||
const result = await waitVerifyDeploymentScale(
|
||||
output,
|
||||
now,
|
||||
deployment.uid,
|
||||
updatedDeployment.scale
|
||||
);
|
||||
if (result instanceof VerifyScaleTimeout) {
|
||||
output.error(
|
||||
`Instance verification timed out (${ms(result.meta.timeout)})`,
|
||||
'verification-timeout'
|
||||
);
|
||||
now.close();
|
||||
return 1;
|
||||
}
|
||||
output.success(`Scale state verified ${verifyStamp()}`);
|
||||
}
|
||||
|
||||
now.close();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import chalk from 'chalk';
|
||||
import { email as regexEmail } from '../../util/input/regexes';
|
||||
import createOutput from '../../util/output/create-output';
|
||||
import wait from '../../util/output/wait';
|
||||
import fatalError from '../../util/fatal-error';
|
||||
import cmd from '../../util/output/cmd.ts';
|
||||
import info from '../../util/output/info';
|
||||
import stamp from '../../util/output/stamp.ts';
|
||||
@@ -65,7 +65,6 @@ export default async function({
|
||||
apiUrl,
|
||||
token,
|
||||
} = {}) {
|
||||
const output = createOutput();
|
||||
const { currentTeam: currentTeamId } = config;
|
||||
|
||||
const stopSpinner = wait('Fetching teams');
|
||||
@@ -100,8 +99,7 @@ export default async function({
|
||||
)}.\nPlease select a team scope using ${cmd('now switch')} or use ${cmd(
|
||||
'--scope'
|
||||
)}`;
|
||||
output.error(err);
|
||||
return 1;
|
||||
return fatalError(err);
|
||||
}
|
||||
|
||||
console.log(
|
||||
|
||||
@@ -15,7 +15,7 @@ try {
|
||||
}
|
||||
import 'core-js/modules/es7.symbol.async-iterator';
|
||||
import { join } from 'path';
|
||||
import { existsSync } from 'fs';
|
||||
import { existsSync, lstatSync } from 'fs';
|
||||
import sourceMap from '@zeit/source-map-support';
|
||||
import { mkdirp } from 'fs-extra';
|
||||
import chalk from 'chalk';
|
||||
@@ -43,7 +43,7 @@ import { handleError } from './util/error';
|
||||
import highlight from './util/output/highlight';
|
||||
import reportError from './util/report-error';
|
||||
import getConfig from './util/get-config';
|
||||
import * as ERRORS from './util/errors';
|
||||
import * as ERRORS from './util/errors-ts';
|
||||
import { NowError } from './util/now-error';
|
||||
import { SENTRY_DSN } from './util/constants.ts';
|
||||
import getUpdateCommand from './util/get-update-command';
|
||||
@@ -375,10 +375,30 @@ const main = async argv_ => {
|
||||
commands.has(targetOrSubcommand);
|
||||
|
||||
if (targetPathExists && subcommandExists) {
|
||||
const fileType = lstatSync(targetPath).isDirectory()
|
||||
? 'subdirectory'
|
||||
: 'file';
|
||||
const plural = targetOrSubcommand + 's';
|
||||
const singular = targetOrSubcommand.endsWith('s')
|
||||
? targetOrSubcommand.slice(0, -1)
|
||||
: '';
|
||||
let alternative = '';
|
||||
if (commands.has(plural)) {
|
||||
alternative = plural;
|
||||
} else if (commands.has(singular)) {
|
||||
alternative = singular;
|
||||
}
|
||||
console.error(
|
||||
error(
|
||||
`The supplied argument ${param(targetOrSubcommand)} is ambiguous. ` +
|
||||
'Both a directory and a subcommand are known'
|
||||
`The supplied argument ${param(targetOrSubcommand)} is ambiguous.` +
|
||||
`\nIf you wish to deploy the ${fileType} ${param(
|
||||
targetOrSubcommand
|
||||
)}, first run "cd ${targetOrSubcommand}". ` +
|
||||
(alternative
|
||||
? `\nIf you wish to use the subcommand ${param(
|
||||
targetOrSubcommand
|
||||
)}, use ${param(alternative)} instead.`
|
||||
: '')
|
||||
)
|
||||
);
|
||||
return 1;
|
||||
@@ -519,7 +539,12 @@ const main = async argv_ => {
|
||||
|
||||
let scope = argv['--scope'] || argv['--team'] || localConfig.scope;
|
||||
|
||||
if (process.env.NOW_ORG_ID || !scope) {
|
||||
const targetCommand = commands.get(subcommand);
|
||||
|
||||
if (
|
||||
!['login', 'logout'].includes(targetCommand) &&
|
||||
(process.env.NOW_ORG_ID || !scope)
|
||||
) {
|
||||
const client = new Client({ apiUrl, token });
|
||||
const link = await getLinkedOrg(client, output);
|
||||
|
||||
@@ -532,8 +557,6 @@ const main = async argv_ => {
|
||||
}
|
||||
}
|
||||
|
||||
const targetCommand = commands.get(subcommand);
|
||||
|
||||
if (
|
||||
typeof scope === 'string' &&
|
||||
targetCommand !== 'login' &&
|
||||
|
||||
@@ -4,10 +4,6 @@ export type ThenArg<T> = T extends Promise<infer U> ? U : T;
|
||||
|
||||
export type Config = NowConfig;
|
||||
|
||||
export interface Dictionary<T> {
|
||||
[key: string]: T;
|
||||
}
|
||||
|
||||
export interface NowContext {
|
||||
argv: string[];
|
||||
apiUrl: string;
|
||||
@@ -106,7 +102,7 @@ export type DeploymentScale = {
|
||||
};
|
||||
|
||||
export type NpmDeployment = {
|
||||
id: string;
|
||||
uid: string;
|
||||
url: string;
|
||||
name: string;
|
||||
type: 'NPM';
|
||||
@@ -119,7 +115,7 @@ export type NpmDeployment = {
|
||||
};
|
||||
|
||||
export type StaticDeployment = {
|
||||
id: string;
|
||||
uid: string;
|
||||
url: string;
|
||||
name: string;
|
||||
type: 'STATIC';
|
||||
@@ -131,7 +127,7 @@ export type StaticDeployment = {
|
||||
};
|
||||
|
||||
export type DockerDeployment = {
|
||||
id: string;
|
||||
uid: string;
|
||||
url: string;
|
||||
name: string;
|
||||
type: 'DOCKER';
|
||||
@@ -229,6 +225,7 @@ export interface Project {
|
||||
devCommand?: string | null;
|
||||
framework?: string | null;
|
||||
rootDirectory?: string | null;
|
||||
latestDeployments?: Partial<Deployment>[];
|
||||
}
|
||||
|
||||
export interface Org {
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { Deployment } from '../../types';
|
||||
import { Output } from '../output';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
import Client from '../client';
|
||||
import createAlias from './create-alias';
|
||||
import deploymentShouldCopyScale from './deployment-should-copy-scale';
|
||||
import deploymentShouldDownscale from './deployment-should-downscale';
|
||||
import findAliasByAliasOrId from './find-alias-by-alias-or-id';
|
||||
import getDeploymentDownscalePresets from './get-deployment-downscale-presets';
|
||||
import getDeploymentFromAlias from './get-deployment-from-alias';
|
||||
import isDomainExternal from '../domains/is-domain-external';
|
||||
import setDeploymentScale from '../scale/set-deployment-scale';
|
||||
import setupDomain from '../domains/setup-domain';
|
||||
import stamp from '../output/stamp';
|
||||
import waitForScale from '../scale/wait-verify-deployment-scale';
|
||||
|
||||
const NOW_SH_REGEX = /\.now\.sh$/;
|
||||
|
||||
@@ -12,11 +21,71 @@ export default async function assignAlias(
|
||||
client: Client,
|
||||
deployment: Deployment,
|
||||
alias: string,
|
||||
contextName: string
|
||||
contextName: string,
|
||||
noVerify: boolean
|
||||
) {
|
||||
const prevAlias = await findAliasByAliasOrId(output, client, alias);
|
||||
let externalDomain = false;
|
||||
|
||||
// Check if the alias is a custom domain, because then
|
||||
// If there was a previous deployment, we should fetch it to scale and downscale later
|
||||
let prevDeployment = await getDeploymentFromAlias(
|
||||
client,
|
||||
contextName,
|
||||
prevAlias,
|
||||
deployment
|
||||
);
|
||||
|
||||
// If there is an alias laying around that points to a deleted
|
||||
// deployment, we need to account for it here.
|
||||
if (prevDeployment instanceof ERRORS.DeploymentNotFound) {
|
||||
prevDeployment = null;
|
||||
}
|
||||
|
||||
if (prevDeployment instanceof Error) {
|
||||
return prevDeployment;
|
||||
}
|
||||
|
||||
// If there was a prev deployment that wasn't static we have to check if we should scale
|
||||
if (
|
||||
prevDeployment !== null &&
|
||||
prevDeployment.type !== 'STATIC' &&
|
||||
deployment.type !== 'STATIC'
|
||||
) {
|
||||
if (deploymentShouldCopyScale(prevDeployment, deployment)) {
|
||||
const scaleStamp = stamp();
|
||||
const result = await setDeploymentScale(
|
||||
output,
|
||||
client,
|
||||
deployment.uid,
|
||||
prevDeployment.scale,
|
||||
deployment.url
|
||||
);
|
||||
if (result instanceof Error) {
|
||||
return result;
|
||||
}
|
||||
|
||||
output.log(
|
||||
`Scale rules copied from previous alias ${
|
||||
prevDeployment.url
|
||||
} ${scaleStamp()}`
|
||||
);
|
||||
if (!noVerify) {
|
||||
const result = await waitForScale(
|
||||
output,
|
||||
client,
|
||||
deployment.uid,
|
||||
prevDeployment.scale
|
||||
);
|
||||
if (result instanceof ERRORS.VerifyScaleTimeout) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
output.debug(`Both deployments have the same scaling rules.`);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the alias is a custom domain and if case we have a positive
|
||||
// we have to configure the DNS records and certificate
|
||||
if (alias.indexOf('.') !== -1 && !NOW_SH_REGEX.test(alias)) {
|
||||
// Now the domain shouldn't be available and it might or might not belong to the user
|
||||
@@ -38,6 +107,23 @@ export default async function assignAlias(
|
||||
alias,
|
||||
externalDomain
|
||||
);
|
||||
if (record instanceof Error) {
|
||||
return record;
|
||||
}
|
||||
|
||||
// Downscale if the previous deployment is not static and doesn't have the minimal presets
|
||||
if (prevDeployment !== null && prevDeployment.type !== 'STATIC') {
|
||||
if (await deploymentShouldDownscale(output, client, prevDeployment)) {
|
||||
await setDeploymentScale(
|
||||
output,
|
||||
client,
|
||||
prevDeployment.uid,
|
||||
getDeploymentDownscalePresets(prevDeployment),
|
||||
prevDeployment.url
|
||||
);
|
||||
output.log(`Previous deployment ${prevDeployment.url} downscaled`);
|
||||
}
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Deployment } from '../../types';
|
||||
import { Output } from '../output';
|
||||
import * as ERRORS from '../errors';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
import Client from '../client';
|
||||
import createCertForAlias from '../certs/create-cert-for-alias';
|
||||
|
||||
@@ -62,7 +62,7 @@ async function performCreateAlias(
|
||||
) {
|
||||
try {
|
||||
return await client.fetch<AliasRecord>(
|
||||
`/now/deployments/${deployment.id}/aliases`,
|
||||
`/now/deployments/${deployment.uid}/aliases`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: { alias },
|
||||
@@ -78,7 +78,7 @@ async function performCreateAlias(
|
||||
if (error.code === 'deployment_not_found') {
|
||||
return new ERRORS.DeploymentNotFound({
|
||||
context: contextName,
|
||||
id: deployment.id,
|
||||
id: deployment.uid,
|
||||
});
|
||||
}
|
||||
if (error.code === 'gone') {
|
||||
|
||||
11
packages/now-cli/src/util/alias/deployment-is-aliased.ts
Normal file
11
packages/now-cli/src/util/alias/deployment-is-aliased.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { Deployment } from '../../types';
|
||||
import Client from '../client';
|
||||
import getAliases from './get-aliases';
|
||||
|
||||
export default async function deploymentIsAliased(
|
||||
client: Client,
|
||||
deployment: Deployment
|
||||
) {
|
||||
const aliases = await getAliases(client);
|
||||
return aliases.some(alias => alias.deploymentId === deployment.uid);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Deployment } from '../../types';
|
||||
import getScaleForDC from '../scale/get-scale-for-dc';
|
||||
|
||||
export default function shouldCopyScalingAttributes(
|
||||
origin: Deployment,
|
||||
dest: Deployment
|
||||
) {
|
||||
if (origin.version === 2 || dest.version === 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
(origin.type !== 'STATIC' &&
|
||||
getScaleForDC('bru1', origin).min !== getScaleForDC('bru1', dest).min) ||
|
||||
getScaleForDC('bru1', origin).max !== getScaleForDC('bru1', dest).max ||
|
||||
getScaleForDC('gru1', origin).min !== getScaleForDC('gru1', dest).min ||
|
||||
getScaleForDC('gru1', origin).max !== getScaleForDC('gru1', dest).max ||
|
||||
getScaleForDC('sfo1', origin).min !== getScaleForDC('sfo1', dest).min ||
|
||||
getScaleForDC('sfo1', origin).max !== getScaleForDC('sfo1', dest).max ||
|
||||
getScaleForDC('iad1', origin).min !== getScaleForDC('iad1', dest).min ||
|
||||
getScaleForDC('iad1', origin).max !== getScaleForDC('iad1', dest).max
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import deploymentIsAliased from './deployment-is-aliased';
|
||||
import { Output } from '../output';
|
||||
import Client from '../client';
|
||||
import getScaleForDC from '../scale/get-scale-for-dc';
|
||||
import { Deployment } from '../../types';
|
||||
|
||||
export default async function deploymentShouldDownscale(
|
||||
output: Output,
|
||||
client: Client,
|
||||
deployment: Deployment
|
||||
) {
|
||||
const isAliased = await deploymentIsAliased(client, deployment);
|
||||
output.debug(`Previous deployment is aliased: ${isAliased.toString()}`);
|
||||
|
||||
if (
|
||||
(deployment.type === 'DOCKER' && !!deployment.slot) ||
|
||||
deployment.version === 2 ||
|
||||
deployment.type === 'STATIC'
|
||||
) {
|
||||
// Don't downscale a previous slot or builds deployment
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
!isAliased &&
|
||||
Object.keys(deployment.scale).reduce(
|
||||
(result, dc) =>
|
||||
result ||
|
||||
getScaleForDC(dc, deployment).min !== 0 ||
|
||||
getScaleForDC(dc, deployment).max !== 1,
|
||||
false
|
||||
)
|
||||
);
|
||||
}
|
||||
72
packages/now-cli/src/util/alias/get-deployment-by-alias.ts
Normal file
72
packages/now-cli/src/util/alias/get-deployment-by-alias.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import path from 'path';
|
||||
import chalk from 'chalk';
|
||||
import Client from '../client';
|
||||
import { Output } from '../output';
|
||||
import { User, Config } from '../../types';
|
||||
import getDeploymentsByAppName from '../deploy/get-deployments-by-appname';
|
||||
import getDeploymentByIdOrHost from '../deploy/get-deployment-by-id-or-host';
|
||||
|
||||
async function getAppLastDeployment(
|
||||
output: Output,
|
||||
client: Client,
|
||||
appName: string,
|
||||
user: User,
|
||||
contextName: string
|
||||
) {
|
||||
output.debug(`Looking for deployments matching app ${appName}`);
|
||||
const deployments = await getDeploymentsByAppName(client, appName);
|
||||
const deploymentItem = deployments
|
||||
.sort((a, b) => b.created - a.created)
|
||||
.filter(dep => dep.state === 'READY' && dep.creator.uid === user.uid)[0];
|
||||
|
||||
// Try to fetch deployment details
|
||||
if (deploymentItem) {
|
||||
return getDeploymentByIdOrHost(client, contextName, deploymentItem.uid);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function getDeploymentForAlias(
|
||||
client: Client,
|
||||
output: Output,
|
||||
args: string[],
|
||||
localConfigPath: string | undefined,
|
||||
user: User,
|
||||
contextName: string,
|
||||
localConfig: Config
|
||||
) {
|
||||
const cancelWait = output.spinner(
|
||||
`Fetching deployment to alias in ${chalk.bold(contextName)}`
|
||||
);
|
||||
|
||||
// When there are no args at all we try to get the targets from the config
|
||||
if (args.length === 2) {
|
||||
const [deploymentId] = args;
|
||||
const deployment = await getDeploymentByIdOrHost(
|
||||
client,
|
||||
contextName,
|
||||
deploymentId
|
||||
);
|
||||
cancelWait();
|
||||
return deployment;
|
||||
}
|
||||
|
||||
const appName =
|
||||
(localConfig && localConfig.name) ||
|
||||
path.basename(path.resolve(process.cwd(), localConfigPath || ''));
|
||||
|
||||
if (!appName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const deployment = await getAppLastDeployment(
|
||||
output,
|
||||
client,
|
||||
appName,
|
||||
user,
|
||||
contextName
|
||||
);
|
||||
cancelWait();
|
||||
return deployment;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { DockerDeployment, NpmDeployment } from '../../types';
|
||||
|
||||
export default function getDeploymentDownscalePresets(
|
||||
deployment: DockerDeployment | NpmDeployment
|
||||
) {
|
||||
return Object.keys(deployment.scale).reduce(
|
||||
(result, dc) =>
|
||||
Object.assign(result, {
|
||||
[dc]: { min: 0, max: 1 },
|
||||
}),
|
||||
{}
|
||||
);
|
||||
}
|
||||
50
packages/now-cli/src/util/alias/get-deployment-for-alias.ts
Normal file
50
packages/now-cli/src/util/alias/get-deployment-for-alias.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
import getAppLastDeployment from '../deploy/get-app-last-deployment';
|
||||
import getAppName from '../deploy/get-app-name';
|
||||
import fetchDeploymentByIdOrHost from '../deploy/get-deployment-by-id-or-host';
|
||||
import Client from '../client';
|
||||
import { Output } from '../output';
|
||||
import { User, Config } from '../../types';
|
||||
|
||||
export default async function getDeploymentForAlias(
|
||||
client: Client,
|
||||
output: Output,
|
||||
args: string[],
|
||||
localConfigPath: string | undefined,
|
||||
user: User,
|
||||
contextName: string,
|
||||
localConfig: Config
|
||||
) {
|
||||
const cancelWait = output.spinner(
|
||||
`Fetching deployment to alias in ${chalk.bold(contextName)}`
|
||||
);
|
||||
|
||||
// When there are no args at all we try to get the targets from the config
|
||||
if (args.length === 2) {
|
||||
const [deploymentId] = args;
|
||||
const deployment = await fetchDeploymentByIdOrHost(
|
||||
client,
|
||||
contextName,
|
||||
deploymentId
|
||||
);
|
||||
cancelWait();
|
||||
return deployment;
|
||||
}
|
||||
|
||||
const appName = await getAppName(output, localConfig, localConfigPath);
|
||||
|
||||
if (!appName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const deployment = await getAppLastDeployment(
|
||||
output,
|
||||
client,
|
||||
appName,
|
||||
user,
|
||||
contextName
|
||||
);
|
||||
cancelWait();
|
||||
return deployment;
|
||||
}
|
||||
16
packages/now-cli/src/util/alias/get-deployment-from-alias.ts
Normal file
16
packages/now-cli/src/util/alias/get-deployment-from-alias.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import Client from '../client';
|
||||
import { Deployment, Alias } from '../../types';
|
||||
import fetchDeploymentByIdOrHost from '../deploy/get-deployment-by-id-or-host';
|
||||
|
||||
export default async function fetchDeploymentFromAlias(
|
||||
client: Client,
|
||||
contextName: string,
|
||||
prevAlias: Alias | null,
|
||||
currentDeployment: Deployment
|
||||
) {
|
||||
return prevAlias &&
|
||||
prevAlias.deploymentId &&
|
||||
prevAlias.deploymentId !== currentDeployment.uid
|
||||
? fetchDeploymentByIdOrHost(client, contextName, prevAlias.deploymentId)
|
||||
: null;
|
||||
}
|
||||
37
packages/now-cli/src/util/alias/get-rules-from-file.ts
Normal file
37
packages/now-cli/src/util/alias/get-rules-from-file.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import path from 'path';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
import humanizePath from '../humanize-path';
|
||||
import readJSONFile from '../read-json-file';
|
||||
import validatePathAliasRules from './validate-path-alias-rules';
|
||||
import { PathRule } from '../../types';
|
||||
|
||||
export default async function getRulesFromFile(filePath: string) {
|
||||
return typeof filePath === 'string' ? readRulesFile(filePath) : null;
|
||||
}
|
||||
|
||||
async function readRulesFile(rulesPath: string) {
|
||||
const fullPath = path.resolve(process.cwd(), rulesPath);
|
||||
const result = (await readJSONFile(fullPath)) as { [key: string]: any };
|
||||
if (result instanceof ERRORS.CantParseJSONFile) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (result === null) {
|
||||
return new ERRORS.FileNotFound(fullPath);
|
||||
}
|
||||
|
||||
if (!result.rules) {
|
||||
return new ERRORS.RulesFileValidationError(
|
||||
humanizePath(fullPath),
|
||||
'Your rules file must include a rules field'
|
||||
);
|
||||
}
|
||||
|
||||
const rules = result.rules as PathRule[];
|
||||
const error = validatePathAliasRules(humanizePath(fullPath), rules);
|
||||
if (error instanceof ERRORS.RulesFileValidationError) {
|
||||
return error;
|
||||
}
|
||||
|
||||
return rules;
|
||||
}
|
||||
28
packages/now-cli/src/util/alias/get-targets-for-alias.ts
Normal file
28
packages/now-cli/src/util/alias/get-targets-for-alias.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import toHost from '../to-host';
|
||||
import { Config } from '../../types';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
|
||||
export function getTargetsForAlias(args: string[], { alias }: Config) {
|
||||
if (args.length) {
|
||||
return targetsToHosts([args[args.length - 1]]);
|
||||
}
|
||||
|
||||
if (!alias) {
|
||||
return new ERRORS.NoAliasInConfig();
|
||||
}
|
||||
|
||||
// Check the type for the option aliases
|
||||
if (typeof alias !== 'string' && !Array.isArray(alias)) {
|
||||
return new ERRORS.InvalidAliasInConfig(alias);
|
||||
}
|
||||
|
||||
return typeof alias === 'string' ? [alias] : alias;
|
||||
}
|
||||
|
||||
function targetsToHosts(targets: string[]) {
|
||||
return targets.map(targetToHost).filter(item => item) as string[];
|
||||
}
|
||||
|
||||
function targetToHost(target: string) {
|
||||
return target.indexOf('.') !== -1 ? toHost(target) : target;
|
||||
}
|
||||
102
packages/now-cli/src/util/alias/upsert-path-alias.ts
Normal file
102
packages/now-cli/src/util/alias/upsert-path-alias.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { Output } from '../output';
|
||||
import { PathRule } from '../../types';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
import Client from '../client';
|
||||
import createCertForAlias from '../certs/create-cert-for-alias';
|
||||
import setupDomain from '../domains/setup-domain';
|
||||
|
||||
const NOW_SH_REGEX = /\.now\.sh$/;
|
||||
|
||||
type AliasRecord = {
|
||||
uid: string;
|
||||
alias: string;
|
||||
created?: string;
|
||||
oldDeploymentId?: string;
|
||||
};
|
||||
|
||||
export default async function upsertPathAlias(
|
||||
output: Output,
|
||||
client: Client,
|
||||
rules: PathRule[],
|
||||
alias: string,
|
||||
contextName: string
|
||||
) {
|
||||
let externalDomain = false;
|
||||
|
||||
if (!NOW_SH_REGEX.test(alias)) {
|
||||
const domainInfo = await setupDomain(output, client, alias, contextName);
|
||||
if (domainInfo instanceof Error) {
|
||||
return domainInfo;
|
||||
}
|
||||
|
||||
externalDomain = domainInfo.serviceType === 'external';
|
||||
}
|
||||
|
||||
const result = await performUpsertPathAlias(
|
||||
output,
|
||||
client,
|
||||
alias,
|
||||
rules,
|
||||
contextName
|
||||
);
|
||||
if (result instanceof ERRORS.CertMissing) {
|
||||
const cert = await createCertForAlias(
|
||||
output,
|
||||
client,
|
||||
contextName,
|
||||
alias,
|
||||
!externalDomain
|
||||
);
|
||||
if (cert instanceof Error) {
|
||||
return cert;
|
||||
}
|
||||
|
||||
return performUpsertPathAlias(output, client, alias, rules, contextName);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function performUpsertPathAlias(
|
||||
output: Output,
|
||||
client: Client,
|
||||
alias: string,
|
||||
rules: PathRule[],
|
||||
contextName: string
|
||||
) {
|
||||
const cancelMessage = output.spinner(
|
||||
`Updating path alias rules for ${alias}`
|
||||
);
|
||||
try {
|
||||
const record = await client.fetch<AliasRecord>(`/now/aliases`, {
|
||||
body: { alias, rules },
|
||||
method: 'POST',
|
||||
});
|
||||
cancelMessage();
|
||||
return record;
|
||||
} catch (error) {
|
||||
cancelMessage();
|
||||
if (error.code === 'cert_missing' || error.code === 'cert_expired') {
|
||||
return new ERRORS.CertMissing(alias);
|
||||
}
|
||||
if (error.status === 409) {
|
||||
return { uid: error.uid, alias: error.alias } as AliasRecord;
|
||||
}
|
||||
if (error.code === 'rule_validation_failed') {
|
||||
return new ERRORS.RuleValidationFailed(error.serverMessage);
|
||||
}
|
||||
if (error.code === 'invalid_alias') {
|
||||
return new ERRORS.InvalidAlias(alias);
|
||||
}
|
||||
if (error.status === 403) {
|
||||
if (error.code === 'alias_in_use') {
|
||||
console.log(error);
|
||||
return new ERRORS.AliasInUse(alias);
|
||||
}
|
||||
if (error.code === 'forbidden') {
|
||||
return new ERRORS.DomainPermissionDenied(alias, contextName);
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
29
packages/now-cli/src/util/alias/validate-path-alias-rules.ts
Normal file
29
packages/now-cli/src/util/alias/validate-path-alias-rules.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { RulesFileValidationError } from '../errors-ts';
|
||||
import { PathRule } from '../../types';
|
||||
|
||||
export default function validatePathAliasRules(
|
||||
location: string,
|
||||
rules: PathRule[]
|
||||
) {
|
||||
if (!Array.isArray(rules)) {
|
||||
return new RulesFileValidationError(location, 'rules must be an array');
|
||||
}
|
||||
if (rules.length === 0) {
|
||||
return new RulesFileValidationError(location, 'empty rules');
|
||||
}
|
||||
|
||||
for (const rule of rules) {
|
||||
if (!(rule instanceof Object)) {
|
||||
return new RulesFileValidationError(
|
||||
location,
|
||||
'all rules must be objects'
|
||||
);
|
||||
}
|
||||
if (!rule.dest) {
|
||||
return new RulesFileValidationError(
|
||||
location,
|
||||
'all rules must have a dest field'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
8
packages/now-cli/src/util/billing/get-credit-cards.js
Normal file
8
packages/now-cli/src/util/billing/get-credit-cards.js
Normal file
@@ -0,0 +1,8 @@
|
||||
//
|
||||
|
||||
export default async function getCreditCards(now) {
|
||||
const payload = await now.fetch('/stripe/sources/');
|
||||
const cards = payload.sources;
|
||||
|
||||
return cards;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
import * as ERRORS from '../errors';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
import Client from '../client';
|
||||
import issueCert from './issue-cert';
|
||||
import wait from '../output/wait';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import chalk from 'chalk';
|
||||
|
||||
import { Cert } from '../../types';
|
||||
import * as ERRORS from '../errors';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
import Client from '../client';
|
||||
import wait from '../output/wait';
|
||||
import mapCertError from './map-cert-error';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Cert } from '../../types';
|
||||
import { Output } from '../output/create-output';
|
||||
import Client from '../client';
|
||||
import * as ERRORS from '../errors';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
|
||||
export default async function getCertById(
|
||||
output: Output,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { stringify } from 'querystring';
|
||||
import { Cert } from '../../types';
|
||||
import { Output } from '../output';
|
||||
import * as ERRORS from '../errors';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
import Client from '../client';
|
||||
|
||||
type Response = {
|
||||
|
||||
@@ -3,7 +3,7 @@ import Client from '../client';
|
||||
import { Output } from '../output';
|
||||
import { Cert } from '../../types';
|
||||
import getCertById from './get-cert-by-id';
|
||||
import { CertNotFound } from '../errors';
|
||||
import { CertNotFound } from '../errors-ts';
|
||||
|
||||
type Response = {
|
||||
certs: Cert[];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import psl from 'psl';
|
||||
import { InvalidDomain } from '../errors';
|
||||
import { InvalidDomain } from '../errors-ts';
|
||||
import isWildcardAlias from '../alias/is-wildcard-alias';
|
||||
import extractDomain from '../alias/extract-domain';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import ms from 'ms';
|
||||
import { parse } from 'psl';
|
||||
import chalk from 'chalk';
|
||||
import * as ERRORS from '../errors';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
import { Output } from '../output';
|
||||
import dnsTable from '../format-dns-table';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as ERRORS from '../errors';
|
||||
import * as ERRORS from '../errors-ts';
|
||||
|
||||
export default function mapCertError(error: any, cns?: string[]) {
|
||||
const errorCode: string = error.code;
|
||||
|
||||
50
packages/now-cli/src/util/check-path.js
Normal file
50
packages/now-cli/src/util/check-path.js
Normal file
@@ -0,0 +1,50 @@
|
||||
// Native
|
||||
import os from 'os';
|
||||
|
||||
import path from 'path';
|
||||
|
||||
const checkPath = async dir => {
|
||||
if (!dir) {
|
||||
return;
|
||||
}
|
||||
|
||||
const home = os.homedir();
|
||||
let location;
|
||||
|
||||
const paths = {
|
||||
home,
|
||||
desktop: path.join(home, 'Desktop'),
|
||||
downloads: path.join(home, 'Downloads'),
|
||||
};
|
||||
|
||||
for (const locationPath in paths) {
|
||||
if (!{}.hasOwnProperty.call(paths, locationPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dir === paths[locationPath]) {
|
||||
location = locationPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (!location) {
|
||||
return;
|
||||
}
|
||||
|
||||
let locationName;
|
||||
|
||||
switch (location) {
|
||||
case 'home':
|
||||
locationName = 'user directory';
|
||||
break;
|
||||
case 'downloads':
|
||||
locationName = 'downloads directory';
|
||||
break;
|
||||
default:
|
||||
locationName = location;
|
||||
}
|
||||
|
||||
throw new Error(`You're trying to deploy your ${locationName}.`);
|
||||
};
|
||||
|
||||
export default checkPath;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user