mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-11 12:57:46 +00:00
Compare commits
21 Commits
vercel@30.
...
@vercel-in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cd77608e8 | ||
|
|
e6b2980eba | ||
|
|
67e20a6ede | ||
|
|
c63679ea0a | ||
|
|
4280166df4 | ||
|
|
18ae78137c | ||
|
|
ebe4058073 | ||
|
|
942e76840e | ||
|
|
57515d2d07 | ||
|
|
ef30a46c03 | ||
|
|
113b8ac87b | ||
|
|
b56ac2717d | ||
|
|
aa8957ab10 | ||
|
|
c6c19354e8 | ||
|
|
96b2502133 | ||
|
|
2df0262675 | ||
|
|
1e47bbf32f | ||
|
|
00813a3945 | ||
|
|
a73ec6343f | ||
|
|
4bd70d4b6e | ||
|
|
c7bcea4081 |
7
.github/workflows/release.yml
vendored
7
.github/workflows/release.yml
vendored
@@ -23,6 +23,9 @@ jobs:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Fetch git tags
|
||||
run: git fetch origin 'refs/tags/*:refs/tags/*'
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
@@ -47,8 +50,8 @@ jobs:
|
||||
id: changesets
|
||||
uses: changesets/action@v1
|
||||
with:
|
||||
version: pnpm version:prepare
|
||||
publish: pnpm release
|
||||
version: pnpm ci:version
|
||||
publish: pnpm ci:publish
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
|
||||
NPM_CONFIG_PROVENANCE: 'true'
|
||||
|
||||
20
.github/workflows/test.yml
vendored
20
.github/workflows/test.yml
vendored
@@ -97,11 +97,21 @@ jobs:
|
||||
if: matrix.runner != 'windows-latest'
|
||||
run: echo | openssl s_client -showcerts -servername 'api.vercel.com' -connect 76.76.21.21:443
|
||||
|
||||
conclusion:
|
||||
summary:
|
||||
name: Summary
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
if: always()
|
||||
needs:
|
||||
- test
|
||||
runs-on: ubuntu-latest
|
||||
name: E2E
|
||||
steps:
|
||||
- name: Done
|
||||
run: echo "Done."
|
||||
- name: Check All
|
||||
run: |-
|
||||
for status in ${{ join(needs.*.result, ' ') }}
|
||||
do
|
||||
if [ "$status" != "success" ] && [ "$status" != "skipped" ]
|
||||
then
|
||||
echo "Some checks failed"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
220
examples/nextjs/package-lock.json
generated
220
examples/nextjs/package-lock.json
generated
@@ -8,9 +8,9 @@
|
||||
"name": "nextjs",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"eslint": "8.40.0",
|
||||
"eslint-config-next": "13.4.3",
|
||||
"next": "13.4.3",
|
||||
"eslint": "8.41.0",
|
||||
"eslint-config-next": "13.4.4",
|
||||
"next": "13.4.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
}
|
||||
@@ -71,9 +71,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.40.0.tgz",
|
||||
"integrity": "sha512-ElyB54bJIhXQYVKjDSvCkPO1iU1tSAeVQJbllWJq1XQSmmA4dgFk8CbiBGpiOPxleE48vDogxCtmMYku4HSVLA==",
|
||||
"version": "8.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz",
|
||||
"integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==",
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
@@ -109,22 +109,22 @@
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "13.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.3.tgz",
|
||||
"integrity": "sha512-pa1ErjyFensznttAk3EIv77vFbfSYT6cLzVRK5jx4uiRuCQo+m2wCFAREaHKIy63dlgvOyMlzh6R8Inu8H3KrQ=="
|
||||
"version": "13.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.4.tgz",
|
||||
"integrity": "sha512-q/y7VZj/9YpgzDe64Zi6rY1xPizx80JjlU2BTevlajtaE3w1LqweH1gGgxou2N7hdFosXHjGrI4OUvtFXXhGLg=="
|
||||
},
|
||||
"node_modules/@next/eslint-plugin-next": {
|
||||
"version": "13.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.4.3.tgz",
|
||||
"integrity": "sha512-5B0uOnh7wyUY9vNNdIA6NUvWozhrZaTMZOzdirYAefqD0ZBK5C/h3+KMYdCKrR7JrXGvVpWnHtv54b3dCzwICA==",
|
||||
"version": "13.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.4.4.tgz",
|
||||
"integrity": "sha512-5jnh7q6I15efnjR/rR+/TGTc9hn53g3JTbEjAMjmeQiExKqEUgIXqrHI5zlTNlNyzCPkBB860/ctxXheZaF2Vw==",
|
||||
"dependencies": {
|
||||
"glob": "7.1.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "13.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.3.tgz",
|
||||
"integrity": "sha512-yx18udH/ZmR4Bw4M6lIIPE3JxsAZwo04iaucEfA2GMt1unXr2iodHUX/LAKNyi6xoLP2ghi0E+Xi1f4Qb8f1LQ==",
|
||||
"version": "13.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.4.tgz",
|
||||
"integrity": "sha512-xfjgXvp4KalNUKZMHmsFxr1Ug+aGmmO6NWP0uoh4G3WFqP/mJ1xxfww0gMOeMeSq/Jyr5k7DvoZ2Pv+XOITTtw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -137,9 +137,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-x64": {
|
||||
"version": "13.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.3.tgz",
|
||||
"integrity": "sha512-Mi8xJWh2IOjryAM1mx18vwmal9eokJ2njY4nDh04scy37F0LEGJ/diL6JL6kTXi0UfUCGbMsOItf7vpReNiD2A==",
|
||||
"version": "13.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.4.tgz",
|
||||
"integrity": "sha512-ZY9Ti1hkIwJsxGus3nlubIkvYyB0gNOYxKrfsOrLEqD0I2iCX8D7w8v6QQZ2H+dDl6UT29oeEUdDUNGk4UEpfg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -152,9 +152,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||
"version": "13.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.3.tgz",
|
||||
"integrity": "sha512-aBvtry4bxJ1xwKZ/LVPeBGBwWVwxa4bTnNkRRw6YffJnn/f4Tv4EGDPaVeYHZGQVA56wsGbtA6nZMuWs/EIk4Q==",
|
||||
"version": "13.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.4.tgz",
|
||||
"integrity": "sha512-+KZnDeMShYkpkqAvGCEDeqYTRADJXc6SY1jWXz+Uo6qWQO/Jd9CoyhTJwRSxvQA16MoYzvILkGaDqirkRNctyA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -167,9 +167,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-musl": {
|
||||
"version": "13.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.3.tgz",
|
||||
"integrity": "sha512-krT+2G3kEsEUvZoYte3/2IscscDraYPc2B+fDJFipPktJmrv088Pei/RjrhWm5TMIy5URYjZUoDZdh5k940Dyw==",
|
||||
"version": "13.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.4.tgz",
|
||||
"integrity": "sha512-evC1twrny2XDT4uOftoubZvW3EG0zs0ZxMwEtu/dDGVRO5n5pT48S8qqEIBGBUZYu/Xx4zzpOkIxx1vpWdE+9A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -182,9 +182,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-gnu": {
|
||||
"version": "13.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.3.tgz",
|
||||
"integrity": "sha512-AMdFX6EKJjC0G/CM6hJvkY8wUjCcbdj3Qg7uAQJ7PVejRWaVt0sDTMavbRfgMchx8h8KsAudUCtdFkG9hlEClw==",
|
||||
"version": "13.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.4.tgz",
|
||||
"integrity": "sha512-PX706XcCHr2FfkyhP2lpf+pX/tUvq6/ke7JYnnr0ykNdEMo+sb7cC/o91gnURh4sPYSiZJhsF2gbIqg9rciOHQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -197,9 +197,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-musl": {
|
||||
"version": "13.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.3.tgz",
|
||||
"integrity": "sha512-jySgSXE48shaLtcQbiFO9ajE9mqz7pcAVLnVLvRIlUHyQYR/WyZdK8ehLs65Mz6j9cLrJM+YdmdJPyV4WDaz2g==",
|
||||
"version": "13.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.4.tgz",
|
||||
"integrity": "sha512-TKUUx3Ftd95JlHV6XagEnqpT204Y+IsEa3awaYIjayn0MOGjgKZMZibqarK3B1FsMSPaieJf2FEAcu9z0yT5aA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -212,9 +212,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||
"version": "13.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.3.tgz",
|
||||
"integrity": "sha512-5DxHo8uYcaADiE9pHrg8o28VMt/1kR8voDehmfs9AqS0qSClxAAl+CchjdboUvbCjdNWL1MISCvEfKY2InJ3JA==",
|
||||
"version": "13.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.4.tgz",
|
||||
"integrity": "sha512-FP8AadgSq4+HPtim7WBkCMGbhr5vh9FePXiWx9+YOdjwdQocwoCK5ZVC3OW8oh3TWth6iJ0AXJ/yQ1q1cwSZ3A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -227,9 +227,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||
"version": "13.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.3.tgz",
|
||||
"integrity": "sha512-LaqkF3d+GXRA5X6zrUjQUrXm2MN/3E2arXBtn5C7avBCNYfm9G3Xc646AmmmpN3DJZVaMYliMyCIQCMDEzk80w==",
|
||||
"version": "13.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.4.tgz",
|
||||
"integrity": "sha512-3WekVmtuA2MCdcAOrgrI+PuFiFURtSyyrN1I3UPtS0ckR2HtLqyqmS334Eulf15g1/bdwMteePdK363X/Y9JMg==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -242,9 +242,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "13.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.3.tgz",
|
||||
"integrity": "sha512-jglUk/x7ZWeOJWlVoKyIAkHLTI+qEkOriOOV+3hr1GyiywzcqfI7TpFSiwC7kk1scOiH7NTFKp8mA3XPNO9bDw==",
|
||||
"version": "13.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.4.tgz",
|
||||
"integrity": "sha512-AHRITu/CrlQ+qzoqQtEMfaTu7GHaQ6bziQln/pVWpOYC1wU+Mq6VQQFlsDtMCnDztPZtppAXdvvbNS7pcfRzlw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -289,9 +289,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgr/utils": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.0.tgz",
|
||||
"integrity": "sha512-2OCURAmRtdlL8iUDTypMrrxfwe8frXTeXaxGsVOaYtc/wrUyk8Z/0OBetM7cdlsy7ZFWlMX72VogKeh+A4Xcjw==",
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.1.tgz",
|
||||
"integrity": "sha512-JOqwkgFEyi+OROIyq7l4Jy28h/WwhDnG/cPkXG2Z1iFbubB6jsHW1NDvmyOzTBxHr3yg68YGirmh1JUgMqa+9w==",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.3",
|
||||
"fast-glob": "^3.2.12",
|
||||
@@ -308,9 +308,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rushstack/eslint-patch": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz",
|
||||
"integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg=="
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.0.tgz",
|
||||
"integrity": "sha512-IthPJsJR85GhOkp3Hvp8zFOPK5ynKn6STyHa/WZpioK7E1aYDiBzpqQPrngc14DszIUkIrdd3k9Iu0XSzlP/1w=="
|
||||
},
|
||||
"node_modules/@swc/helpers": {
|
||||
"version": "0.5.1",
|
||||
@@ -326,13 +326,13 @@
|
||||
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "5.59.6",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.6.tgz",
|
||||
"integrity": "sha512-7pCa6al03Pv1yf/dUg/s1pXz/yGMUBAw5EeWqNTFiSueKvRNonze3hma3lhdsOrQcaOXhbk5gKu2Fludiho9VA==",
|
||||
"version": "5.59.7",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.7.tgz",
|
||||
"integrity": "sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.59.6",
|
||||
"@typescript-eslint/types": "5.59.6",
|
||||
"@typescript-eslint/typescript-estree": "5.59.6",
|
||||
"@typescript-eslint/scope-manager": "5.59.7",
|
||||
"@typescript-eslint/types": "5.59.7",
|
||||
"@typescript-eslint/typescript-estree": "5.59.7",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -352,12 +352,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "5.59.6",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.6.tgz",
|
||||
"integrity": "sha512-gLbY3Le9Dxcb8KdpF0+SJr6EQ+hFGYFl6tVY8VxLPFDfUZC7BHFw+Vq7bM5lE9DwWPfx4vMWWTLGXgpc0mAYyQ==",
|
||||
"version": "5.59.7",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz",
|
||||
"integrity": "sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.59.6",
|
||||
"@typescript-eslint/visitor-keys": "5.59.6"
|
||||
"@typescript-eslint/types": "5.59.7",
|
||||
"@typescript-eslint/visitor-keys": "5.59.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@@ -368,9 +368,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "5.59.6",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.6.tgz",
|
||||
"integrity": "sha512-tH5lBXZI7T2MOUgOWFdVNUILsI02shyQvfzG9EJkoONWugCG77NDDa1EeDGw7oJ5IvsTAAGVV8I3Tk2PNu9QfA==",
|
||||
"version": "5.59.7",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.7.tgz",
|
||||
"integrity": "sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==",
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
@@ -380,12 +380,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "5.59.6",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.6.tgz",
|
||||
"integrity": "sha512-vW6JP3lMAs/Tq4KjdI/RiHaaJSO7IUsbkz17it/Rl9Q+WkQ77EOuOnlbaU8kKfVIOJxMhnRiBG+olE7f3M16DA==",
|
||||
"version": "5.59.7",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz",
|
||||
"integrity": "sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.59.6",
|
||||
"@typescript-eslint/visitor-keys": "5.59.6",
|
||||
"@typescript-eslint/types": "5.59.7",
|
||||
"@typescript-eslint/visitor-keys": "5.59.7",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -406,11 +406,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "5.59.6",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.6.tgz",
|
||||
"integrity": "sha512-zEfbFLzB9ETcEJ4HZEEsCR9HHeNku5/Qw1jSS5McYJv5BR+ftYXwFFAH5Al+xkGaZEqowMwl7uoJjQb1YSPF8Q==",
|
||||
"version": "5.59.7",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz",
|
||||
"integrity": "sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.59.6",
|
||||
"@typescript-eslint/types": "5.59.7",
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -591,9 +591,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axe-core": {
|
||||
"version": "4.7.1",
|
||||
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.1.tgz",
|
||||
"integrity": "sha512-sCXXUhA+cljomZ3ZAwb8i1p3oOlkABzPy08ZDAoGcYuvtBPlQ1Ytde129ArXyHWDhfeewq7rlx9F+cUx2SSlkg==",
|
||||
"version": "4.7.2",
|
||||
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.2.tgz",
|
||||
"integrity": "sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
@@ -696,9 +696,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001488",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001488.tgz",
|
||||
"integrity": "sha512-NORIQuuL4xGpIy6iCCQGN4iFjlBXtfKWIenlUuyZJumLRIindLb7wXM+GO8erEhb7vXfcnf4BAg2PrSDN5TNLQ==",
|
||||
"version": "1.0.30001489",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz",
|
||||
"integrity": "sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -908,9 +908,9 @@
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.14.0",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.0.tgz",
|
||||
"integrity": "sha512-+DCows0XNwLDcUhbFJPdlQEVnT2zXlCv7hPxemTz86/O+B/hCQ+mb7ydkPKiflpVraqLPCAfu7lDy+hBXueojw==",
|
||||
"version": "5.14.1",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz",
|
||||
"integrity": "sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.4",
|
||||
"tapable": "^2.2.0"
|
||||
@@ -1034,14 +1034,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.40.0.tgz",
|
||||
"integrity": "sha512-bvR+TsP9EHL3TqNtj9sCNJVAFK3fBN8Q7g5waghxyRsPLIMwL73XSKnZFK0hk/O2ANC+iAoq6PWMQ+IfBAJIiQ==",
|
||||
"version": "8.41.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz",
|
||||
"integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.4.0",
|
||||
"@eslint/eslintrc": "^2.0.3",
|
||||
"@eslint/js": "8.40.0",
|
||||
"@eslint/js": "8.41.0",
|
||||
"@humanwhocodes/config-array": "^0.11.8",
|
||||
"@humanwhocodes/module-importer": "^1.0.1",
|
||||
"@nodelib/fs.walk": "^1.2.8",
|
||||
@@ -1061,13 +1061,12 @@
|
||||
"find-up": "^5.0.0",
|
||||
"glob-parent": "^6.0.2",
|
||||
"globals": "^13.19.0",
|
||||
"grapheme-splitter": "^1.0.4",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.2.0",
|
||||
"import-fresh": "^3.0.0",
|
||||
"imurmurhash": "^0.1.4",
|
||||
"is-glob": "^4.0.0",
|
||||
"is-path-inside": "^3.0.3",
|
||||
"js-sdsl": "^4.1.4",
|
||||
"js-yaml": "^4.1.0",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"levn": "^0.4.1",
|
||||
@@ -1090,11 +1089,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-next": {
|
||||
"version": "13.4.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.4.3.tgz",
|
||||
"integrity": "sha512-1lXwdFi29fKxzeugof/TUE7lpHyJQt5+U4LaUHyvQfHjvsWO77vFNicJv5sX6k0VDVSbnfz0lw+avxI+CinbMg==",
|
||||
"version": "13.4.4",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.4.4.tgz",
|
||||
"integrity": "sha512-z/PMbm6L0iC/fwISULxe8IVy4DtNqZk2wQY711o35klenq70O6ns82A8yuMVCFjHC0DIyB2lyugesRtuk9u8dQ==",
|
||||
"dependencies": {
|
||||
"@next/eslint-plugin-next": "13.4.3",
|
||||
"@next/eslint-plugin-next": "13.4.4",
|
||||
"@rushstack/eslint-patch": "^1.1.3",
|
||||
"@typescript-eslint/parser": "^5.42.0",
|
||||
"eslint-import-resolver-node": "^0.3.6",
|
||||
@@ -1765,10 +1764,10 @@
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
|
||||
},
|
||||
"node_modules/grapheme-splitter": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
|
||||
"integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
|
||||
"node_modules/graphemer": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
||||
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
|
||||
},
|
||||
"node_modules/has": {
|
||||
"version": "1.0.3",
|
||||
@@ -2256,15 +2255,6 @@
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||
},
|
||||
"node_modules/js-sdsl": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz",
|
||||
"integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/js-sdsl"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -2463,11 +2453,11 @@
|
||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "13.4.3",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-13.4.3.tgz",
|
||||
"integrity": "sha512-FV3pBrAAnAIfOclTvncw9dDohyeuEEXPe5KNcva91anT/rdycWbgtu3IjUj4n5yHnWK8YEPo0vrUecHmnmUNbA==",
|
||||
"version": "13.4.4",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-13.4.4.tgz",
|
||||
"integrity": "sha512-C5S0ysM0Ily9McL4Jb48nOQHT1BukOWI59uC3X/xCMlYIh9rJZCv7nzG92J6e1cOBqQbKovlpgvHWFmz4eKKEA==",
|
||||
"dependencies": {
|
||||
"@next/env": "13.4.3",
|
||||
"@next/env": "13.4.4",
|
||||
"@swc/helpers": "0.5.1",
|
||||
"busboy": "1.6.0",
|
||||
"caniuse-lite": "^1.0.30001406",
|
||||
@@ -2482,20 +2472,19 @@
|
||||
"node": ">=16.8.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@next/swc-darwin-arm64": "13.4.3",
|
||||
"@next/swc-darwin-x64": "13.4.3",
|
||||
"@next/swc-linux-arm64-gnu": "13.4.3",
|
||||
"@next/swc-linux-arm64-musl": "13.4.3",
|
||||
"@next/swc-linux-x64-gnu": "13.4.3",
|
||||
"@next/swc-linux-x64-musl": "13.4.3",
|
||||
"@next/swc-win32-arm64-msvc": "13.4.3",
|
||||
"@next/swc-win32-ia32-msvc": "13.4.3",
|
||||
"@next/swc-win32-x64-msvc": "13.4.3"
|
||||
"@next/swc-darwin-arm64": "13.4.4",
|
||||
"@next/swc-darwin-x64": "13.4.4",
|
||||
"@next/swc-linux-arm64-gnu": "13.4.4",
|
||||
"@next/swc-linux-arm64-musl": "13.4.4",
|
||||
"@next/swc-linux-x64-gnu": "13.4.4",
|
||||
"@next/swc-linux-x64-musl": "13.4.4",
|
||||
"@next/swc-win32-arm64-msvc": "13.4.4",
|
||||
"@next/swc-win32-ia32-msvc": "13.4.4",
|
||||
"@next/swc-win32-x64-msvc": "13.4.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
"fibers": ">= 3.1.0",
|
||||
"node-sass": "^6.0.0 || ^7.0.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"sass": "^1.3.0"
|
||||
@@ -2507,9 +2496,6 @@
|
||||
"fibers": {
|
||||
"optional": true
|
||||
},
|
||||
"node-sass": {
|
||||
"optional": true
|
||||
},
|
||||
"sass": {
|
||||
"optional": true
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint": "8.40.0",
|
||||
"eslint-config-next": "13.4.3",
|
||||
"next": "13.4.3",
|
||||
"eslint": "8.41.0",
|
||||
"eslint-config-next": "13.4.4",
|
||||
"next": "13.4.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "preact build",
|
||||
"build": "NODE_OPTIONS=--openssl-legacy-provider preact build",
|
||||
"serve": "sirv build --port 8080 --cors --single",
|
||||
"dev": "preact watch",
|
||||
"lint": "eslint src",
|
||||
"test": "jest"
|
||||
},
|
||||
"engines": {
|
||||
"node": "16.x"
|
||||
"node": "18.x"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "preact",
|
||||
@@ -17,19 +17,19 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"enzyme": "^3.10.0",
|
||||
"enzyme-adapter-preact-pure": "^2.0.0",
|
||||
"eslint": "^6.0.1",
|
||||
"eslint-config-preact": "^1.1.0",
|
||||
"jest": "^24.9.0",
|
||||
"jest-preset-preact": "^1.0.0",
|
||||
"preact-cli": "^3.0.0",
|
||||
"sirv-cli": "1.0.3"
|
||||
"enzyme": "^3.11.0",
|
||||
"enzyme-adapter-preact-pure": "^4.1.0",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint-config-preact": "^1.3.0",
|
||||
"jest": "^29.5.0",
|
||||
"jest-preset-preact": "^4.0.4",
|
||||
"preact-cli": "^3.4.5",
|
||||
"sirv-cli": "2.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"preact": "^10.3.2",
|
||||
"preact-render-to-string": "^5.1.4",
|
||||
"preact-router": "^3.2.1"
|
||||
"preact": "^10.15.0",
|
||||
"preact-render-to-string": "6.0.3",
|
||||
"preact-router": "^4.1.1"
|
||||
},
|
||||
"jest": {
|
||||
"preset": "jest-preset-preact",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,10 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['eslint:recommended', 'prettier'],
|
||||
plugins: ['svelte3'],
|
||||
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
|
||||
extends: ['eslint:recommended', 'plugin:svelte/recommended', 'prettier'],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2020
|
||||
ecmaVersion: 2020,
|
||||
extraFileExtensions: ['.svelte']
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
engine-strict=true
|
||||
resolution-mode=highest
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
# create-svelte
|
||||
# SvelteKit Demo app
|
||||
|
||||
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
|
||||
The official demo app for SvelteKit, hosted on Vercel.
|
||||
|
||||
## Creating a project
|
||||
## Deploy Your Own
|
||||
|
||||
If you're seeing this, you've probably already done this step. Congrats!
|
||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fvercel%2Ftree%2Fmain%2Fexamples%2Fsveltekit-1&project-name=sveltekit-vercel&repository-name=sveltekit-vercel&demo-title=SvelteKit%20%2B%20Vercel&demo-url=https%3A%2F%2Fsveltekit-template.vercel.app%2F)
|
||||
|
||||
```bash
|
||||
# create a new project in the current directory
|
||||
npm create svelte@latest
|
||||
|
||||
# create a new project in my-app
|
||||
npm create svelte@latest my-app
|
||||
```
|
||||
_Live Example: https://sveltekit-template.vercel.app_
|
||||
|
||||
## Developing
|
||||
|
||||
@@ -35,4 +29,8 @@ npm run build
|
||||
|
||||
You can preview the production build with `npm run preview`.
|
||||
|
||||
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
||||
## Speed Insights
|
||||
|
||||
Once deployed on Vercel, you can benefit from [Speed Insights](https://vercel.com/docs/concepts/speed-insights) simply by navigating to Vercel's dashboard, clicking on the 'Speed Insights' tab, and enabling the product.
|
||||
|
||||
You will get data once your application will be re-deployed and will receive visitors.
|
||||
|
||||
@@ -16,19 +16,20 @@
|
||||
"@fontsource/fira-mono": "^4.5.10",
|
||||
"@neoconfetti/svelte": "^1.0.0",
|
||||
"@playwright/test": "^1.28.1",
|
||||
"@sveltejs/adapter-vercel": "^1.0.0",
|
||||
"@sveltejs/kit": "^1.0.0",
|
||||
"@sveltejs/adapter-vercel": "^3.0.0",
|
||||
"@sveltejs/kit": "^1.5.0",
|
||||
"@types/cookie": "^0.5.1",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
"eslint-plugin-svelte": "^2.26.0",
|
||||
"prettier": "^2.8.0",
|
||||
"prettier-plugin-svelte": "^2.8.1",
|
||||
"svelte": "^3.54.0",
|
||||
"svelte-check": "^2.9.2",
|
||||
"typescript": "^4.9.3",
|
||||
"vite": "^4.0.0",
|
||||
"vitest": "^0.25.3"
|
||||
"svelte-check": "^3.0.1",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^4.3.0",
|
||||
"vitest": "^0.25.3",
|
||||
"web-vitals": "^3.3.1"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ const config = {
|
||||
command: 'npm run build && npm run preview',
|
||||
port: 4173
|
||||
},
|
||||
testDir: 'tests'
|
||||
testDir: 'tests',
|
||||
testMatch: /(.+\.)?(test|spec)\.[jt]s/
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
786
examples/sveltekit-1/pnpm-lock.yaml
generated
786
examples/sveltekit-1/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
15
examples/sveltekit-1/src/app.d.ts
vendored
15
examples/sveltekit-1/src/app.d.ts
vendored
@@ -1,9 +1,12 @@
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
// and what to do when importing types
|
||||
declare namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface Platform {}
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
|
||||
63
examples/sveltekit-1/src/lib/vitals.js
Normal file
63
examples/sveltekit-1/src/lib/vitals.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { onCLS, onFCP, onFID, onLCP, onTTFB } from 'web-vitals';
|
||||
|
||||
const vitalsUrl = 'https://vitals.vercel-analytics.com/v1/vitals';
|
||||
|
||||
function getConnectionSpeed() {
|
||||
// @ts-ignore
|
||||
return navigator?.connection?.effectiveType ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("web-vitals").Metric} metric
|
||||
* @param {{ params: { [s: string]: any; } | ArrayLike<any>; path: string; analyticsId: string; debug: boolean; }} options
|
||||
*/
|
||||
function sendToAnalytics(metric, options) {
|
||||
const page = Object.entries(options.params).reduce(
|
||||
(acc, [key, value]) => acc.replace(value, `[${key}]`),
|
||||
options.path
|
||||
);
|
||||
|
||||
const body = {
|
||||
dsn: options.analyticsId,
|
||||
id: metric.id,
|
||||
page,
|
||||
href: location.href,
|
||||
event_name: metric.name,
|
||||
value: metric.value.toString(),
|
||||
speed: getConnectionSpeed()
|
||||
};
|
||||
|
||||
if (options.debug) {
|
||||
console.log('[Web Vitals]', metric.name, JSON.stringify(body, null, 2));
|
||||
}
|
||||
|
||||
const blob = new Blob([new URLSearchParams(body).toString()], {
|
||||
// This content type is necessary for `sendBeacon`
|
||||
type: 'application/x-www-form-urlencoded'
|
||||
});
|
||||
if (navigator.sendBeacon) {
|
||||
navigator.sendBeacon(vitalsUrl, blob);
|
||||
} else
|
||||
fetch(vitalsUrl, {
|
||||
body: blob,
|
||||
method: 'POST',
|
||||
credentials: 'omit',
|
||||
keepalive: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} options
|
||||
*/
|
||||
export function webVitals(options) {
|
||||
try {
|
||||
console.log(`[Web Vitals] for page ${options.path}`);
|
||||
onFID((metric) => sendToAnalytics(metric, options));
|
||||
onTTFB((metric) => sendToAnalytics(metric, options));
|
||||
onLCP((metric) => sendToAnalytics(metric, options));
|
||||
onCLS((metric) => sendToAnalytics(metric, options));
|
||||
onFCP((metric) => sendToAnalytics(metric, options));
|
||||
} catch (err) {
|
||||
console.error(`[Web Vitals] for page ${options.path}`, err);
|
||||
}
|
||||
}
|
||||
6
examples/sveltekit-1/src/routes/+layout.server.js
Normal file
6
examples/sveltekit-1/src/routes/+layout.server.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import { env } from '$env/dynamic/private';
|
||||
|
||||
/** @type {import('./$types').LayoutServerLoad} */
|
||||
export function load() {
|
||||
return { analyticsId: env.VERCEL_ANALYTICS_ID };
|
||||
}
|
||||
@@ -1,6 +1,20 @@
|
||||
<script>
|
||||
import { browser } from '$app/environment';
|
||||
import { page } from '$app/stores';
|
||||
import { webVitals } from '$lib/vitals';
|
||||
import Header from './Header.svelte';
|
||||
import './styles.css';
|
||||
|
||||
/** @type {import('./$types').LayoutServerData} */
|
||||
export let data;
|
||||
|
||||
$: if (browser && data?.analyticsId) {
|
||||
webVitals({
|
||||
path: $page.url.pathname,
|
||||
params: $page.params,
|
||||
analyticsId: data.analyticsId
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="app">
|
||||
|
||||
@@ -107,11 +107,11 @@
|
||||
<a class="how-to-play" href="/sverdle/how-to-play">How to play</a>
|
||||
|
||||
<div class="grid" class:playing={!won} class:bad-guess={form?.badGuess}>
|
||||
{#each Array(6) as _, row}
|
||||
{#each Array.from(Array(6).keys()) as row (row)}
|
||||
{@const current = row === i}
|
||||
<h2 class="visually-hidden">Row {row + 1}</h2>
|
||||
<div class="row" class:current>
|
||||
{#each Array(5) as _, column}
|
||||
{#each Array.from(Array(5).keys()) as column (column)}
|
||||
{@const answer = data.answers[row]?.[column]}
|
||||
{@const value = data.guesses[row]?.[column] ?? ''}
|
||||
{@const selected = current && column === data.guesses[row].length}
|
||||
|
||||
@@ -2,5 +2,5 @@ import { expect, test } from '@playwright/test';
|
||||
|
||||
test('about page has expected h1', async ({ page }) => {
|
||||
await page.goto('/about');
|
||||
expect(await page.textContent('h1')).toBe('About this app');
|
||||
await expect(page.getByRole('heading', { name: 'About this app' })).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
/** @type {import('vite').UserConfig} */
|
||||
const config = {
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()],
|
||||
test: {
|
||||
include: ['src/**/*.{test,spec}.{js,ts}']
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
});
|
||||
|
||||
8
internals/constants/CHANGELOG.md
Normal file
8
internals/constants/CHANGELOG.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# @vercel-internals/constants
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`c7bcea408`](https://github.com/vercel/vercel/commit/c7bcea408131df2d65338e50ce319a6d8e4a8a82)]:
|
||||
- @vercel/build-utils@6.7.4
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@vercel-internals/constants",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"types": "dist/index.d.ts",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"@vercel/build-utils": "6.7.4",
|
||||
"@vercel/routing-utils": "2.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
9
internals/types/CHANGELOG.md
Normal file
9
internals/types/CHANGELOG.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# @vercel-internals/types
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`c7bcea408`](https://github.com/vercel/vercel/commit/c7bcea408131df2d65338e50ce319a6d8e4a8a82)]:
|
||||
- @vercel/build-utils@6.7.4
|
||||
- @vercel-internals/constants@1.0.1
|
||||
30
internals/types/index.d.ts
vendored
30
internals/types/index.d.ts
vendored
@@ -355,7 +355,7 @@ export interface Project extends ProjectSettings {
|
||||
link?: ProjectLinkData;
|
||||
alias?: ProjectAliasTarget[];
|
||||
latestDeployments?: Partial<Deployment>[];
|
||||
lastRollbackTarget: RollbackTarget | null;
|
||||
lastAliasRequest?: LastAliasRequest | null;
|
||||
}
|
||||
|
||||
export interface Org {
|
||||
@@ -365,8 +365,19 @@ export interface Org {
|
||||
}
|
||||
|
||||
export interface ProjectLink {
|
||||
/**
|
||||
* ID of the Vercel Project.
|
||||
*/
|
||||
projectId: string;
|
||||
/**
|
||||
* User or Team ID of the owner of the Vercel Project.
|
||||
*/
|
||||
orgId: string;
|
||||
/**
|
||||
* When linked as a repository, contains the absolute path
|
||||
* to the root directory of the repository.
|
||||
*/
|
||||
repoRoot?: string;
|
||||
}
|
||||
|
||||
export interface PaginationOptions {
|
||||
@@ -376,7 +387,7 @@ export interface PaginationOptions {
|
||||
}
|
||||
|
||||
export type ProjectLinkResult =
|
||||
| { status: 'linked'; org: Org; project: Project }
|
||||
| { status: 'linked'; org: Org; project: Project; repoRoot?: string }
|
||||
| { status: 'not_linked'; org: null; project: null }
|
||||
| {
|
||||
status: 'error';
|
||||
@@ -390,6 +401,9 @@ export type ProjectLinkResult =
|
||||
| 'MISSING_PROJECT_SETTINGS';
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated - `RollbackJobStatus` has been replace by `LastAliasRequest['jobStatus']`.
|
||||
*/
|
||||
export type RollbackJobStatus =
|
||||
| 'pending'
|
||||
| 'in-progress'
|
||||
@@ -397,6 +411,10 @@ export type RollbackJobStatus =
|
||||
| 'failed'
|
||||
| 'skipped';
|
||||
|
||||
/**
|
||||
* @deprecated - `RollbackTarget` has been renamed to `LastAliasRequest` so it can
|
||||
* be shared with "promote".
|
||||
*/
|
||||
export interface RollbackTarget {
|
||||
fromDeploymentId: string;
|
||||
jobStatus: RollbackJobStatus;
|
||||
@@ -404,6 +422,14 @@ export interface RollbackTarget {
|
||||
toDeploymentId: string;
|
||||
}
|
||||
|
||||
export interface LastAliasRequest {
|
||||
fromDeploymentId: string;
|
||||
jobStatus: 'pending' | 'in-progress' | 'succeeded' | 'failed' | 'skipped';
|
||||
requestedAt: number;
|
||||
toDeploymentId: string;
|
||||
type: 'rollback' | 'promote';
|
||||
}
|
||||
|
||||
export interface Token {
|
||||
id: string;
|
||||
name: string;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@vercel-internals/types",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"types": "index.d.ts",
|
||||
"main": "index.d.ts",
|
||||
"dependencies": {
|
||||
"@types/node": "14.14.31",
|
||||
"@vercel-internals/constants": "1.0.0",
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"@vercel-internals/constants": "1.0.1",
|
||||
"@vercel/build-utils": "6.7.4",
|
||||
"@vercel/routing-utils": "2.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"source-map-support": "0.5.12",
|
||||
"ts-eager": "2.0.2",
|
||||
"ts-jest": "29.1.0",
|
||||
"turbo": "1.9.8",
|
||||
"turbo": "1.9.9",
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"scripts": {
|
||||
@@ -48,8 +48,8 @@
|
||||
"prettier-check": "prettier --check .",
|
||||
"prepare": "husky install",
|
||||
"pack": "cd utils && node -r ts-eager/register ./pack.ts",
|
||||
"version:prepare": "changeset version && pnpm install --no-frozen-lockfile",
|
||||
"release": "changeset publish"
|
||||
"ci:version": "changeset version && pnpm install --no-frozen-lockfile",
|
||||
"ci:publish": "pnpm publish -r && changeset tag"
|
||||
},
|
||||
"lint-staged": {
|
||||
"./{*,{api,packages,test,utils}/**/*}.{js,ts}": [
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @vercel/build-utils
|
||||
|
||||
## 6.7.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Remove usage of `env` from Edge Functions and Middleware ([#10018](https://github.com/vercel/vercel/pull/10018))
|
||||
|
||||
## 6.7.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "6.7.3",
|
||||
"version": "6.7.4",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -27,12 +27,6 @@ export class EdgeFunction {
|
||||
*/
|
||||
files: Files;
|
||||
|
||||
/**
|
||||
* Extra environment variables in use for the user code, to be
|
||||
* assigned to the edge function.
|
||||
*/
|
||||
envVarsInUse?: string[];
|
||||
|
||||
/**
|
||||
* Extra binary files to be included in the edge function
|
||||
*/
|
||||
@@ -50,7 +44,6 @@ export class EdgeFunction {
|
||||
this.deploymentTarget = params.deploymentTarget;
|
||||
this.entrypoint = params.entrypoint;
|
||||
this.files = params.files;
|
||||
this.envVarsInUse = params.envVarsInUse;
|
||||
this.assets = params.assets;
|
||||
this.regions = params.regions;
|
||||
this.framework = params.framework;
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# vercel
|
||||
|
||||
## 30.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- New `vc promote` command ([#9984](https://github.com/vercel/vercel/pull/9984))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Support `deploy` subcommand in "repo linked" mode ([#10013](https://github.com/vercel/vercel/pull/10013))
|
||||
|
||||
- [cli] Update `vc rollback` to use `lastRequestAlias` instead of `lastRollbackTarget` ([#10019](https://github.com/vercel/vercel/pull/10019))
|
||||
|
||||
- Fix `--cwd` flag with a relative path for `env`, `link`, `promote`, and `rollback` subcommands ([#10031](https://github.com/vercel/vercel/pull/10031))
|
||||
|
||||
- Updated dependencies [[`c6c19354e`](https://github.com/vercel/vercel/commit/c6c19354e852cfc1338b223058c4b07fdc71c723), [`b56ac2717`](https://github.com/vercel/vercel/commit/b56ac2717d6769eb400f9746f0a05431929b4501), [`c63679ea0`](https://github.com/vercel/vercel/commit/c63679ea0a6bc48c0759ccf3c0c0a8106bd324f0), [`c7bcea408`](https://github.com/vercel/vercel/commit/c7bcea408131df2d65338e50ce319a6d8e4a8a82)]:
|
||||
- @vercel/next@3.8.6
|
||||
- @vercel/build-utils@6.7.4
|
||||
- @vercel/node@2.14.4
|
||||
- @vercel/remix-builder@1.8.11
|
||||
- @vercel/static-build@1.3.33
|
||||
|
||||
## 30.0.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "30.0.0",
|
||||
"version": "30.1.0",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -32,16 +32,16 @@
|
||||
"node": ">= 14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"@vercel/build-utils": "6.7.4",
|
||||
"@vercel/go": "2.5.1",
|
||||
"@vercel/hydrogen": "0.0.64",
|
||||
"@vercel/next": "3.8.5",
|
||||
"@vercel/node": "2.14.3",
|
||||
"@vercel/next": "3.8.6",
|
||||
"@vercel/node": "2.14.4",
|
||||
"@vercel/python": "3.1.60",
|
||||
"@vercel/redwood": "1.1.15",
|
||||
"@vercel/remix-builder": "1.8.10",
|
||||
"@vercel/remix-builder": "1.8.11",
|
||||
"@vercel/ruby": "1.3.76",
|
||||
"@vercel/static-build": "1.3.32"
|
||||
"@vercel/static-build": "1.3.33"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alex_neo/jest-expect-message": "1.0.5",
|
||||
@@ -85,10 +85,10 @@
|
||||
"@types/which": "3.0.0",
|
||||
"@types/write-json-file": "2.2.1",
|
||||
"@types/yauzl-promise": "2.1.0",
|
||||
"@vercel-internals/constants": "1.0.0",
|
||||
"@vercel-internals/constants": "1.0.1",
|
||||
"@vercel-internals/get-package-json": "1.0.0",
|
||||
"@vercel-internals/types": "1.0.0",
|
||||
"@vercel/client": "12.6.0",
|
||||
"@vercel-internals/types": "1.0.1",
|
||||
"@vercel/client": "12.6.1",
|
||||
"@vercel/error-utils": "1.0.10",
|
||||
"@vercel/frameworks": "1.4.2",
|
||||
"@vercel/fs-detectors": "3.9.3",
|
||||
@@ -119,6 +119,7 @@
|
||||
"escape-html": "1.0.3",
|
||||
"esm": "3.1.4",
|
||||
"execa": "3.2.0",
|
||||
"expect": "29.5.0",
|
||||
"express": "4.17.1",
|
||||
"fast-deep-equal": "3.1.3",
|
||||
"find-up": "4.1.0",
|
||||
|
||||
@@ -24,6 +24,7 @@ export const help = () => `
|
||||
ls | list [app] Lists deployments
|
||||
login [email] Logs into your account or creates a new one
|
||||
logout Logs out of your account
|
||||
promote [url|id] Promote an existing deployment to current
|
||||
pull [path] Pull your Project Settings from the cloud
|
||||
redeploy [url|id] Rebuild and deploy a previous deployment.
|
||||
rollback [url|id] Quickly revert back to a previous deployment
|
||||
|
||||
@@ -133,7 +133,7 @@ const help = () => {
|
||||
};
|
||||
|
||||
export default async function main(client: Client): Promise<number> {
|
||||
const { output } = client;
|
||||
const { cwd, output } = client;
|
||||
|
||||
// Ensure that `vc build` is not being invoked recursively
|
||||
if (process.env.__VERCEL_BUILD_RUNNING) {
|
||||
@@ -165,8 +165,6 @@ export default async function main(client: Client): Promise<number> {
|
||||
return 2;
|
||||
}
|
||||
|
||||
const cwd = process.cwd();
|
||||
|
||||
// Build `target` influences which environment variables will be used
|
||||
const target = argv['--prod'] ? 'production' : 'preview';
|
||||
const yes = Boolean(argv['--yes']);
|
||||
|
||||
@@ -2,7 +2,7 @@ import ms from 'ms';
|
||||
import fs from 'fs-extra';
|
||||
import bytes from 'bytes';
|
||||
import chalk from 'chalk';
|
||||
import { join, resolve, basename } from 'path';
|
||||
import { join, resolve } from 'path';
|
||||
import {
|
||||
fileNameSymbol,
|
||||
VALID_ARCHIVE_FORMATS,
|
||||
@@ -130,24 +130,19 @@ export default async (client: Client): Promise<number> => {
|
||||
if (argv._.length > 0) {
|
||||
// If path is relative: resolve
|
||||
// if path is absolute: clear up strange `/` etc
|
||||
paths = argv._.map(item => resolve(process.cwd(), item));
|
||||
paths = argv._.map(item => resolve(client.cwd, item));
|
||||
} else {
|
||||
paths = [process.cwd()];
|
||||
paths = [client.cwd];
|
||||
}
|
||||
|
||||
// check paths
|
||||
const pathValidation = await validatePaths(client, paths);
|
||||
|
||||
if (!pathValidation.valid) {
|
||||
return pathValidation.exitCode;
|
||||
}
|
||||
|
||||
let localConfig = client.localConfig || readLocalConfig(paths[0]);
|
||||
|
||||
for (const path of paths) {
|
||||
try {
|
||||
await fs.stat(path);
|
||||
} catch (err) {
|
||||
output.error(
|
||||
`The specified file or directory "${basename(path)}" does not exist.`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (localConfig) {
|
||||
const { version } = localConfig;
|
||||
const file = highlight(localConfig[fileNameSymbol]!);
|
||||
@@ -176,14 +171,7 @@ export default async (client: Client): Promise<number> => {
|
||||
|
||||
const quiet = !client.stdout.isTTY;
|
||||
|
||||
// check paths
|
||||
const pathValidation = await validatePaths(client, paths);
|
||||
|
||||
if (!pathValidation.valid) {
|
||||
return pathValidation.exitCode;
|
||||
}
|
||||
|
||||
const { path } = pathValidation;
|
||||
let { path: cwd } = pathValidation;
|
||||
const autoConfirm = argv['--yes'];
|
||||
|
||||
// deprecate --name
|
||||
@@ -217,7 +205,7 @@ export default async (client: Client): Promise<number> => {
|
||||
|
||||
// build `--prebuilt`
|
||||
if (argv['--prebuilt']) {
|
||||
const prebuiltExists = await fs.pathExists(join(path, '.vercel/output'));
|
||||
const prebuiltExists = await fs.pathExists(join(cwd, '.vercel/output'));
|
||||
if (!prebuiltExists) {
|
||||
error(
|
||||
`The ${param(
|
||||
@@ -229,7 +217,7 @@ export default async (client: Client): Promise<number> => {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const prebuiltBuild = await getPrebuiltJson(path);
|
||||
const prebuiltBuild = await getPrebuiltJson(cwd);
|
||||
|
||||
// Ensure that there was not a build error
|
||||
const prebuiltError =
|
||||
@@ -272,7 +260,7 @@ export default async (client: Client): Promise<number> => {
|
||||
}
|
||||
|
||||
// retrieve `project` and `org` from .vercel
|
||||
const link = await getLinkedProject(client, path);
|
||||
const link = await getLinkedProject(client, cwd);
|
||||
|
||||
if (link.status === 'error') {
|
||||
return link.exitCode;
|
||||
@@ -289,7 +277,7 @@ export default async (client: Client): Promise<number> => {
|
||||
autoConfirm ||
|
||||
(await confirm(
|
||||
client,
|
||||
`Set up and deploy ${chalk.cyan(`“${toHumanPath(path)}”`)}?`,
|
||||
`Set up and deploy ${chalk.cyan(`“${toHumanPath(cwd)}”`)}?`,
|
||||
true
|
||||
));
|
||||
|
||||
@@ -336,7 +324,7 @@ export default async (client: Client): Promise<number> => {
|
||||
|
||||
if (typeof projectOrNewProjectName === 'string') {
|
||||
newProjectName = projectOrNewProjectName;
|
||||
rootDirectory = await inputRootDirectory(client, path, autoConfirm);
|
||||
rootDirectory = await inputRootDirectory(client, cwd, autoConfirm);
|
||||
} else {
|
||||
project = projectOrNewProjectName;
|
||||
rootDirectory = project.rootDirectory;
|
||||
@@ -344,8 +332,8 @@ export default async (client: Client): Promise<number> => {
|
||||
|
||||
// we can already link the project
|
||||
await linkFolderToProject(
|
||||
output,
|
||||
path,
|
||||
client,
|
||||
cwd,
|
||||
{
|
||||
projectId: project.id,
|
||||
orgId: org.id,
|
||||
@@ -357,6 +345,11 @@ export default async (client: Client): Promise<number> => {
|
||||
}
|
||||
}
|
||||
|
||||
// For repo-style linking, reset the path to the root of the repository
|
||||
if (link.status === 'linked' && link.repoRoot) {
|
||||
cwd = link.repoRoot;
|
||||
}
|
||||
|
||||
// At this point `org` should be populated
|
||||
if (!org) {
|
||||
throw new Error(`"org" is not defined`);
|
||||
@@ -371,14 +364,14 @@ export default async (client: Client): Promise<number> => {
|
||||
// and upload the entire directory.
|
||||
const sourcePath =
|
||||
rootDirectory && !sourceFilesOutsideRootDirectory
|
||||
? join(path, rootDirectory)
|
||||
: path;
|
||||
? join(cwd, rootDirectory)
|
||||
: cwd;
|
||||
|
||||
if (
|
||||
rootDirectory &&
|
||||
(await validateRootDirectory(
|
||||
output,
|
||||
path,
|
||||
cwd,
|
||||
sourcePath,
|
||||
project
|
||||
? `To change your Project Settings, go to https://vercel.com/${org?.slug}/${project.name}/settings`
|
||||
@@ -391,7 +384,7 @@ export default async (client: Client): Promise<number> => {
|
||||
// If Root Directory is used we'll try to read the config
|
||||
// from there instead and use it if it exists.
|
||||
if (rootDirectory) {
|
||||
const rootDirectoryConfig = readLocalConfig(join(path, rootDirectory));
|
||||
const rootDirectoryConfig = readLocalConfig(join(cwd, rootDirectory));
|
||||
|
||||
if (rootDirectoryConfig) {
|
||||
debug(`Read local config from root directory (${rootDirectory})`);
|
||||
@@ -467,7 +460,7 @@ export default async (client: Client): Promise<number> => {
|
||||
parseMeta(argv['--meta'])
|
||||
);
|
||||
|
||||
const gitMetadata = await createGitMeta(path, output, project);
|
||||
const gitMetadata = await createGitMeta(cwd, output, project);
|
||||
|
||||
// Merge dotenv config, `env` from vercel.json, and `--env` / `-e` arguments
|
||||
const deploymentEnv = Object.assign(
|
||||
@@ -560,11 +553,11 @@ export default async (client: Client): Promise<number> => {
|
||||
client,
|
||||
now,
|
||||
contextName,
|
||||
[sourcePath],
|
||||
sourcePath,
|
||||
createArgs,
|
||||
org,
|
||||
!project,
|
||||
path,
|
||||
cwd,
|
||||
archive
|
||||
);
|
||||
|
||||
@@ -596,11 +589,11 @@ export default async (client: Client): Promise<number> => {
|
||||
client,
|
||||
now,
|
||||
contextName,
|
||||
[sourcePath],
|
||||
sourcePath,
|
||||
createArgs,
|
||||
org,
|
||||
false,
|
||||
path
|
||||
cwd
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
3
packages/cli/src/commands/env/index.ts
vendored
3
packages/cli/src/commands/env/index.ts
vendored
@@ -130,10 +130,9 @@ export default async function main(client: Client) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
const cwd = argv['--cwd'] || process.cwd();
|
||||
const subArgs = argv._.slice(1);
|
||||
const { subcommand, args } = getSubcommand(subArgs, COMMAND_CONFIG);
|
||||
const { output, config } = client;
|
||||
const { cwd, output, config } = client;
|
||||
|
||||
const target = argv['--environment']?.toLowerCase() || 'development';
|
||||
if (!isValidEnvTarget(target)) {
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
parseRepoUrl,
|
||||
printRemoteUrls,
|
||||
} from '../../util/git/connect-git-provider';
|
||||
import validatePaths from '../../util/validate-paths';
|
||||
|
||||
interface GitRepoCheckParams {
|
||||
client: Client;
|
||||
@@ -56,7 +55,7 @@ export default async function connect(
|
||||
project: Project | undefined,
|
||||
org: Org | undefined
|
||||
) {
|
||||
const { output } = client;
|
||||
const { cwd, output } = client;
|
||||
const confirm = Boolean(argv['--yes']);
|
||||
const repoArg = argv._[1];
|
||||
|
||||
@@ -77,19 +76,11 @@ export default async function connect(
|
||||
return 1;
|
||||
}
|
||||
|
||||
let paths = [process.cwd()];
|
||||
|
||||
const validate = await validatePaths(client, paths);
|
||||
if (!validate.valid) {
|
||||
return validate.exitCode;
|
||||
}
|
||||
const { path } = validate;
|
||||
|
||||
const gitProviderLink = project.link;
|
||||
client.config.currentTeam = org.type === 'team' ? org.id : undefined;
|
||||
|
||||
// get project from .git
|
||||
const gitConfigPath = join(path, '.git/config');
|
||||
const gitConfigPath = join(cwd, '.git/config');
|
||||
const gitConfig = await parseGitConfig(gitConfigPath, output);
|
||||
|
||||
if (repoArg) {
|
||||
|
||||
@@ -6,7 +6,6 @@ import getInvalidSubcommand from '../../util/get-invalid-subcommand';
|
||||
import handleError from '../../util/handle-error';
|
||||
import logo from '../../util/output/logo';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import validatePaths from '../../util/validate-paths';
|
||||
import connect from './connect';
|
||||
import disconnect from './disconnect';
|
||||
|
||||
@@ -81,16 +80,9 @@ export default async function main(client: Client) {
|
||||
subcommand = argv._[0];
|
||||
const args = argv._.slice(1);
|
||||
const autoConfirm = Boolean(argv['--yes']);
|
||||
const { output } = client;
|
||||
const { cwd, output } = client;
|
||||
|
||||
let paths = [process.cwd()];
|
||||
const pathValidation = await validatePaths(client, paths);
|
||||
if (!pathValidation.valid) {
|
||||
return pathValidation.exitCode;
|
||||
}
|
||||
const { path } = pathValidation;
|
||||
|
||||
const linkedProject = await ensureLink('git', client, path, { autoConfirm });
|
||||
const linkedProject = await ensureLink('git', client, cwd, { autoConfirm });
|
||||
if (typeof linkedProject === 'number') {
|
||||
return linkedProject;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ export default new Map([
|
||||
['ls', 'list'],
|
||||
['project', 'project'],
|
||||
['projects', 'project'],
|
||||
['promote', 'promote'],
|
||||
['pull', 'pull'],
|
||||
['redeploy', 'redeploy'],
|
||||
['remove', 'remove'],
|
||||
|
||||
@@ -90,7 +90,7 @@ export default async function main(client: Client) {
|
||||
)} instead`
|
||||
);
|
||||
} else {
|
||||
cwd = process.cwd();
|
||||
cwd = client.cwd;
|
||||
}
|
||||
|
||||
if (argv['--repo']) {
|
||||
|
||||
@@ -15,7 +15,6 @@ import getCommandFlags from '../util/get-command-flags';
|
||||
import { getPkgName, getCommandName } from '../util/pkg-name';
|
||||
import Client from '../util/client';
|
||||
import { Deployment } from '@vercel/client';
|
||||
import validatePaths from '../util/validate-paths';
|
||||
import { getLinkedProject } from '../util/projects/link';
|
||||
import { ensureLink } from '../util/link/ensure-link';
|
||||
import getScope from '../util/get-scope';
|
||||
@@ -94,7 +93,7 @@ export default async function main(client: Client) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const { output, config } = client;
|
||||
const { cwd, output, config } = client;
|
||||
|
||||
if ('--confirm' in argv) {
|
||||
output.warn('`--confirm` is deprecated, please use `--yes` instead');
|
||||
@@ -115,19 +114,10 @@ export default async function main(client: Client) {
|
||||
|
||||
const autoConfirm = !!argv['--yes'];
|
||||
const prod = argv['--prod'] || false;
|
||||
|
||||
const meta = parseMeta(argv['--meta']);
|
||||
|
||||
let paths = [process.cwd()];
|
||||
const pathValidation = await validatePaths(client, paths);
|
||||
if (!pathValidation.valid) {
|
||||
return pathValidation.exitCode;
|
||||
}
|
||||
|
||||
const { path } = pathValidation;
|
||||
|
||||
// retrieve `project` and `org` from .vercel
|
||||
let link = await getLinkedProject(client, path);
|
||||
let link = await getLinkedProject(client, cwd);
|
||||
|
||||
if (link.status === 'error') {
|
||||
return link.exitCode;
|
||||
@@ -146,7 +136,7 @@ export default async function main(client: Client) {
|
||||
// If there's no linked project and user doesn't pass `app` arg,
|
||||
// prompt to link their current directory.
|
||||
if (status === 'not_linked' && !app) {
|
||||
const linkedProject = await ensureLink('list', client, path, {
|
||||
const linkedProject = await ensureLink('list', client, cwd, {
|
||||
autoConfirm,
|
||||
link,
|
||||
});
|
||||
|
||||
121
packages/cli/src/commands/promote/index.ts
Normal file
121
packages/cli/src/commands/promote/index.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import chalk from 'chalk';
|
||||
import type Client from '../../util/client';
|
||||
import getArgs from '../../util/get-args';
|
||||
import getProjectByCwdOrLink from '../../util/projects/get-project-by-cwd-or-link';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import handleError from '../../util/handle-error';
|
||||
import { isErrnoException } from '@vercel/error-utils';
|
||||
import logo from '../../util/output/logo';
|
||||
import ms from 'ms';
|
||||
import requestPromote from './request-promote';
|
||||
import promoteStatus from './status';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
${chalk.bold(`${logo} ${getPkgName()} promote`)} [deployment id/url]
|
||||
|
||||
Promote an existing deployment to current.
|
||||
|
||||
${chalk.dim('Options:')}
|
||||
|
||||
-h, --help Output usage information
|
||||
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
|
||||
'FILE'
|
||||
)} Path to the local ${'`vercel.json`'} file
|
||||
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
|
||||
'DIR'
|
||||
)} Path to the global ${'`.vercel`'} directory
|
||||
-d, --debug Debug mode [off]
|
||||
--no-color No color mode [off]
|
||||
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
|
||||
'TOKEN'
|
||||
)} Login token
|
||||
--timeout=${chalk.bold.underline(
|
||||
'TIME'
|
||||
)} Time to wait for promotion completion [3m]
|
||||
-y, --yes Skip questions when setting up new project using default scope and settings
|
||||
|
||||
${chalk.dim('Examples:')}
|
||||
|
||||
${chalk.gray('–')} Show the status of any current pending promotions
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} promote`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} promote status`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} promote status <project>`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} promote status --timeout 30s`)}
|
||||
|
||||
${chalk.gray('–')} Promote a deployment using id or url
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} promote <deployment id/url>`)}
|
||||
`);
|
||||
};
|
||||
|
||||
/**
|
||||
* `vc promote` command
|
||||
* @param {Client} client
|
||||
* @returns {Promise<number>} Resolves an exit code; 0 on success
|
||||
*/
|
||||
export default async (client: Client): Promise<number> => {
|
||||
let argv;
|
||||
try {
|
||||
argv = getArgs(client.argv.slice(2), {
|
||||
'--timeout': String,
|
||||
'--yes': Boolean,
|
||||
'-y': '--yes',
|
||||
});
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argv['--help'] || argv._[0] === 'help') {
|
||||
help();
|
||||
return 2;
|
||||
}
|
||||
|
||||
// validate the timeout
|
||||
let timeout = argv['--timeout'];
|
||||
if (timeout && ms(timeout) === undefined) {
|
||||
client.output.error(`Invalid timeout "${timeout}"`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const actionOrDeployId = argv._[1] || 'status';
|
||||
|
||||
try {
|
||||
if (actionOrDeployId === 'status') {
|
||||
const project = await getProjectByCwdOrLink({
|
||||
autoConfirm: Boolean(argv['--yes']),
|
||||
client,
|
||||
commandName: 'promote',
|
||||
cwd: client.cwd,
|
||||
projectNameOrId: argv._[2],
|
||||
});
|
||||
|
||||
return await promoteStatus({
|
||||
client,
|
||||
project,
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
|
||||
return await requestPromote({
|
||||
client,
|
||||
deployId: actionOrDeployId,
|
||||
timeout,
|
||||
});
|
||||
} catch (err) {
|
||||
if (isErrnoException(err)) {
|
||||
if (err.code === 'ERR_CANCELED') {
|
||||
return 0;
|
||||
}
|
||||
if (err.code === 'ERR_INVALID_CWD' || err.code === 'ERR_LINK_PROJECT') {
|
||||
// do not show the message
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
client.output.prettyError(err);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
57
packages/cli/src/commands/promote/request-promote.ts
Normal file
57
packages/cli/src/commands/promote/request-promote.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import chalk from 'chalk';
|
||||
import type Client from '../../util/client';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import getProjectByDeployment from '../../util/projects/get-project-by-deployment';
|
||||
import ms from 'ms';
|
||||
import promoteStatus from './status';
|
||||
|
||||
/**
|
||||
* Requests a promotion and waits for it complete.
|
||||
* @param {Client} client - The Vercel client instance
|
||||
* @param {string} deployId - The deployment name or id to promote
|
||||
* @param {string} [timeout] - Time to poll for succeeded/failed state
|
||||
* @returns {Promise<number>} Resolves an exit code; 0 on success
|
||||
*/
|
||||
export default async function requestPromote({
|
||||
client,
|
||||
deployId,
|
||||
timeout,
|
||||
}: {
|
||||
client: Client;
|
||||
deployId: string;
|
||||
timeout?: string;
|
||||
}): Promise<number> {
|
||||
const { output } = client;
|
||||
|
||||
const { contextName, deployment, project } = await getProjectByDeployment({
|
||||
client,
|
||||
deployId,
|
||||
output: client.output,
|
||||
});
|
||||
|
||||
// request the promotion
|
||||
await client.fetch(`/v9/projects/${project.id}/promote/${deployment.id}`, {
|
||||
body: {}, // required
|
||||
json: false,
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
if (timeout !== undefined && ms(timeout) === 0) {
|
||||
output.log(
|
||||
`Successfully requested promote of ${chalk.bold(project.name)} to ${
|
||||
deployment.url
|
||||
} (${deployment.id})`
|
||||
);
|
||||
output.log(`To check promote status, run ${getCommandName('promote')}.`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check the status
|
||||
return await promoteStatus({
|
||||
client,
|
||||
contextName,
|
||||
deployment,
|
||||
project,
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
265
packages/cli/src/commands/promote/status.ts
Normal file
265
packages/cli/src/commands/promote/status.ts
Normal file
@@ -0,0 +1,265 @@
|
||||
import chalk from 'chalk';
|
||||
import type Client from '../../util/client';
|
||||
import type {
|
||||
Deployment,
|
||||
LastAliasRequest,
|
||||
PaginationOptions,
|
||||
Project,
|
||||
} from '@vercel-internals/types';
|
||||
import elapsed from '../../util/output/elapsed';
|
||||
import formatDate from '../../util/format-date';
|
||||
import getDeployment from '../../util/get-deployment';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import getProjectByNameOrId from '../../util/projects/get-project-by-id-or-name';
|
||||
import getScope from '../../util/get-scope';
|
||||
import ms from 'ms';
|
||||
import { ProjectNotFound } from '../../util/errors-ts';
|
||||
import renderAliasStatus from '../../util/alias/render-alias-status';
|
||||
import sleep from '../../util/sleep';
|
||||
|
||||
interface DeploymentAlias {
|
||||
alias: {
|
||||
alias: string;
|
||||
deploymentId: string;
|
||||
};
|
||||
id: string;
|
||||
status: 'completed' | 'in-progress' | 'pending' | 'failed';
|
||||
}
|
||||
|
||||
interface AliasesResponse {
|
||||
aliases: DeploymentAlias[];
|
||||
pagination: PaginationOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Continuously checks a deployment status until it has succeeded, failed, or
|
||||
* taken longer than the timeout (default 3 minutes).
|
||||
*
|
||||
* @param {Client} client - The Vercel client instance
|
||||
* @param {string} [contextName] - The scope name; if not specified, it will be
|
||||
* extracted from the `client`
|
||||
* @param {Deployment} [deployment] - Info about the deployment which is used
|
||||
* to display different output following a promotion request
|
||||
* @param {Project} project - Project info instance
|
||||
* @param {string} [timeout] - Milliseconds to poll for succeeded/failed state
|
||||
* @returns {Promise<number>} Resolves an exit code; 0 on success
|
||||
*/
|
||||
export default async function promoteStatus({
|
||||
client,
|
||||
contextName,
|
||||
deployment,
|
||||
project,
|
||||
timeout = '3m',
|
||||
}: {
|
||||
client: Client;
|
||||
contextName?: string;
|
||||
deployment?: Deployment;
|
||||
project: Project;
|
||||
timeout?: string;
|
||||
}): Promise<number> {
|
||||
const { output } = client;
|
||||
const recentThreshold = Date.now() - ms('3m');
|
||||
const promoteTimeout = Date.now() + ms(timeout);
|
||||
let counter = 0;
|
||||
let spinnerMessage = deployment
|
||||
? 'Promote in progress'
|
||||
: `Checking promotion status of ${project.name}`;
|
||||
|
||||
if (!contextName) {
|
||||
({ contextName } = await getScope(client));
|
||||
}
|
||||
|
||||
try {
|
||||
output.spinner(`${spinnerMessage}…`);
|
||||
|
||||
// continuously loop until the promotion has explicitly succeeded, failed,
|
||||
// or timed out
|
||||
for (;;) {
|
||||
const projectCheck = await getProjectByNameOrId(
|
||||
client,
|
||||
project.id,
|
||||
project.accountId,
|
||||
true
|
||||
);
|
||||
if (projectCheck instanceof ProjectNotFound) {
|
||||
throw projectCheck;
|
||||
}
|
||||
|
||||
const {
|
||||
jobStatus,
|
||||
requestedAt,
|
||||
toDeploymentId,
|
||||
type,
|
||||
}: Partial<LastAliasRequest> = projectCheck.lastAliasRequest ?? {};
|
||||
|
||||
if (
|
||||
!jobStatus ||
|
||||
(jobStatus !== 'in-progress' && jobStatus !== 'pending')
|
||||
) {
|
||||
output.stopSpinner();
|
||||
output.log(`${spinnerMessage}…`);
|
||||
}
|
||||
|
||||
if (
|
||||
!jobStatus ||
|
||||
!requestedAt ||
|
||||
!toDeploymentId ||
|
||||
requestedAt < recentThreshold
|
||||
) {
|
||||
output.log('No deployment promotion in progress');
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (jobStatus === 'skipped' && type === 'promote') {
|
||||
output.log('Promote deployment was skipped');
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (jobStatus === 'succeeded') {
|
||||
return await renderJobSucceeded({
|
||||
client,
|
||||
contextName,
|
||||
performingPromote: !!deployment,
|
||||
requestedAt,
|
||||
project,
|
||||
toDeploymentId,
|
||||
});
|
||||
}
|
||||
|
||||
if (jobStatus === 'failed') {
|
||||
return await renderJobFailed({
|
||||
client,
|
||||
contextName,
|
||||
deployment,
|
||||
project,
|
||||
toDeploymentId,
|
||||
});
|
||||
}
|
||||
|
||||
// lastly, if we're not pending/in-progress, then we don't know what
|
||||
// the status is, so bail
|
||||
if (jobStatus !== 'pending' && jobStatus !== 'in-progress') {
|
||||
output.log(`Unknown promote deployment status "${jobStatus}"`);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check if we have been running for too long
|
||||
if (requestedAt < recentThreshold || Date.now() >= promoteTimeout) {
|
||||
output.log(
|
||||
`The promotion exceeded its deadline - rerun ${chalk.bold(
|
||||
`${getPkgName()} promote ${toDeploymentId}`
|
||||
)} to try again`
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// if we've done our first poll and not rolling back, then print the
|
||||
// requested at date/time
|
||||
if (counter++ === 0 && !deployment) {
|
||||
spinnerMessage += ` requested at ${formatDate(requestedAt)}`;
|
||||
}
|
||||
output.spinner(`${spinnerMessage}…`);
|
||||
|
||||
await sleep(250);
|
||||
}
|
||||
} finally {
|
||||
output.stopSpinner();
|
||||
}
|
||||
}
|
||||
|
||||
async function renderJobFailed({
|
||||
client,
|
||||
contextName,
|
||||
deployment,
|
||||
project,
|
||||
toDeploymentId,
|
||||
}: {
|
||||
client: Client;
|
||||
contextName: string;
|
||||
deployment?: Deployment;
|
||||
project: Project;
|
||||
toDeploymentId: string;
|
||||
}) {
|
||||
const { output } = client;
|
||||
|
||||
try {
|
||||
const name = (
|
||||
deployment || (await getDeployment(client, contextName, toDeploymentId))
|
||||
)?.url;
|
||||
output.error(
|
||||
`Failed to remap all aliases to the requested deployment ${name} (${toDeploymentId})`
|
||||
);
|
||||
} catch (e) {
|
||||
output.error(
|
||||
`Failed to remap all aliases to the requested deployment ${toDeploymentId}`
|
||||
);
|
||||
}
|
||||
|
||||
// aliases are paginated, so continuously loop until all of them have been
|
||||
// fetched
|
||||
let nextTimestamp;
|
||||
for (;;) {
|
||||
let url = `/v9/projects/${project.id}/promote/aliases?failedOnly=true&limit=20`;
|
||||
if (nextTimestamp) {
|
||||
url += `&until=${nextTimestamp}`;
|
||||
}
|
||||
|
||||
const { aliases, pagination } = await client.fetch<AliasesResponse>(url);
|
||||
|
||||
for (const { alias, status } of aliases) {
|
||||
output.log(
|
||||
` ${renderAliasStatus(status).padEnd(11)} ${alias.alias} (${
|
||||
alias.deploymentId
|
||||
})`
|
||||
);
|
||||
}
|
||||
|
||||
if (pagination?.next) {
|
||||
nextTimestamp = pagination.next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
async function renderJobSucceeded({
|
||||
client,
|
||||
contextName,
|
||||
performingPromote,
|
||||
project,
|
||||
requestedAt,
|
||||
toDeploymentId,
|
||||
}: {
|
||||
client: Client;
|
||||
contextName: string;
|
||||
performingPromote: boolean;
|
||||
project: Project;
|
||||
requestedAt: number;
|
||||
toDeploymentId: string;
|
||||
}) {
|
||||
const { output } = client;
|
||||
|
||||
// attempt to get the new deployment url
|
||||
let deploymentInfo = '';
|
||||
try {
|
||||
const deployment = await getDeployment(client, contextName, toDeploymentId);
|
||||
deploymentInfo = `${chalk.bold(deployment.url)} (${toDeploymentId})`;
|
||||
} catch (err: any) {
|
||||
output.debug(
|
||||
`Failed to get deployment url for ${toDeploymentId}: ${
|
||||
err?.toString() || err
|
||||
}`
|
||||
);
|
||||
deploymentInfo = chalk.bold(toDeploymentId);
|
||||
}
|
||||
|
||||
const duration = performingPromote ? elapsed(Date.now() - requestedAt) : '';
|
||||
output.log(
|
||||
`Success! ${chalk.bold(
|
||||
project.name
|
||||
)} was promoted to ${deploymentInfo} ${duration}`
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,20 +1,18 @@
|
||||
import chalk from 'chalk';
|
||||
import type Client from '../util/client';
|
||||
import { ensureLink } from '../util/link/ensure-link';
|
||||
import getArgs from '../util/get-args';
|
||||
import { getPkgName } from '../util/pkg-name';
|
||||
import handleError from '../util/handle-error';
|
||||
import logo from '../util/output/logo';
|
||||
import type Client from '../../util/client';
|
||||
import getArgs from '../../util/get-args';
|
||||
import getProjectByCwdOrLink from '../../util/projects/get-project-by-cwd-or-link';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import handleError from '../../util/handle-error';
|
||||
import { isErrnoException } from '@vercel/error-utils';
|
||||
import logo from '../../util/output/logo';
|
||||
import ms from 'ms';
|
||||
import requestRollback from '../util/rollback/request-rollback';
|
||||
import rollbackStatus from '../util/rollback/status';
|
||||
import validatePaths from '../util/validate-paths';
|
||||
import requestRollback from './request-rollback';
|
||||
import rollbackStatus from './status';
|
||||
|
||||
const help = () => {
|
||||
console.log(`
|
||||
${chalk.bold(
|
||||
`${logo} ${getPkgName()} rollback`
|
||||
)} [deploymentId|deploymentName]
|
||||
${chalk.bold(`${logo} ${getPkgName()} rollback`)} [deployment id/url]
|
||||
|
||||
Quickly revert back to a previous deployment.
|
||||
|
||||
@@ -43,6 +41,7 @@ const help = () => {
|
||||
|
||||
${chalk.cyan(`$ ${getPkgName()} rollback`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} rollback status`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} rollback status <project>`)}
|
||||
${chalk.cyan(`$ ${getPkgName()} rollback status --timeout 30s`)}
|
||||
|
||||
${chalk.gray('–')} Rollback a deployment using id or url
|
||||
@@ -60,8 +59,6 @@ export default async (client: Client): Promise<number> => {
|
||||
let argv;
|
||||
try {
|
||||
argv = getArgs(client.argv.slice(2), {
|
||||
'--debug': Boolean,
|
||||
'-d': '--debug',
|
||||
'--timeout': String,
|
||||
'--yes': Boolean,
|
||||
'-y': '--yes',
|
||||
@@ -76,26 +73,6 @@ export default async (client: Client): Promise<number> => {
|
||||
return 2;
|
||||
}
|
||||
|
||||
// ensure the current directory is good
|
||||
const cwd = argv['--cwd'] || process.cwd();
|
||||
const pathValidation = await validatePaths(client, [cwd]);
|
||||
if (!pathValidation.valid) {
|
||||
return pathValidation.exitCode;
|
||||
}
|
||||
|
||||
// ensure the current directory is a linked project
|
||||
const linkedProject = await ensureLink(
|
||||
'rollback',
|
||||
client,
|
||||
pathValidation.path,
|
||||
{
|
||||
autoConfirm: Boolean(argv['--yes']),
|
||||
}
|
||||
);
|
||||
if (typeof linkedProject === 'number') {
|
||||
return linkedProject;
|
||||
}
|
||||
|
||||
// validate the timeout
|
||||
let timeout = argv['--timeout'];
|
||||
if (timeout && ms(timeout) === undefined) {
|
||||
@@ -103,21 +80,42 @@ export default async (client: Client): Promise<number> => {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const { project } = linkedProject;
|
||||
const actionOrDeployId = argv._[1] || 'status';
|
||||
|
||||
if (actionOrDeployId === 'status') {
|
||||
return await rollbackStatus({
|
||||
try {
|
||||
if (actionOrDeployId === 'status') {
|
||||
const project = await getProjectByCwdOrLink({
|
||||
autoConfirm: Boolean(argv['--yes']),
|
||||
client,
|
||||
commandName: 'promote',
|
||||
cwd: client.cwd,
|
||||
projectNameOrId: argv._[2],
|
||||
});
|
||||
|
||||
return await rollbackStatus({
|
||||
client,
|
||||
project,
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
|
||||
return await requestRollback({
|
||||
client,
|
||||
project,
|
||||
deployId: actionOrDeployId,
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
if (isErrnoException(err)) {
|
||||
if (err.code === 'ERR_CANCELED') {
|
||||
return 0;
|
||||
}
|
||||
if (err.code === 'ERR_INVALID_CWD' || err.code === 'ERR_LINK_PROJECT') {
|
||||
// do not show the message
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return await requestRollback({
|
||||
client,
|
||||
deployIdOrUrl: actionOrDeployId,
|
||||
project,
|
||||
timeout,
|
||||
});
|
||||
client.output.prettyError(err);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
56
packages/cli/src/commands/rollback/request-rollback.ts
Normal file
56
packages/cli/src/commands/rollback/request-rollback.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import chalk from 'chalk';
|
||||
import type Client from '../../util/client';
|
||||
import { getCommandName } from '../../util/pkg-name';
|
||||
import getProjectByDeployment from '../../util/projects/get-project-by-deployment';
|
||||
import ms from 'ms';
|
||||
import rollbackStatus from './status';
|
||||
|
||||
/**
|
||||
* Requests a rollback and waits for it complete.
|
||||
* @param {Client} client - The Vercel client instance
|
||||
* @param {string} deployIdOrUrl - The deployment name or id to rollback
|
||||
* @param {string} [timeout] - Time to poll for succeeded/failed state
|
||||
* @returns {Promise<number>} Resolves an exit code; 0 on success
|
||||
*/
|
||||
export default async function requestRollback({
|
||||
client,
|
||||
deployId,
|
||||
timeout,
|
||||
}: {
|
||||
client: Client;
|
||||
deployId: string;
|
||||
timeout?: string;
|
||||
}): Promise<number> {
|
||||
const { output } = client;
|
||||
|
||||
const { contextName, deployment, project } = await getProjectByDeployment({
|
||||
client,
|
||||
deployId,
|
||||
output: client.output,
|
||||
});
|
||||
|
||||
// create the rollback
|
||||
await client.fetch(`/v9/projects/${project.id}/rollback/${deployment.id}`, {
|
||||
body: {}, // required
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
if (timeout !== undefined && ms(timeout) === 0) {
|
||||
output.log(
|
||||
`Successfully requested rollback of ${chalk.bold(project.name)} to ${
|
||||
deployment.url
|
||||
} (${deployment.id})`
|
||||
);
|
||||
output.log(`To check rollback status, run ${getCommandName('rollback')}.`);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check the status
|
||||
return await rollbackStatus({
|
||||
client,
|
||||
contextName,
|
||||
deployment,
|
||||
project,
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
@@ -1,18 +1,21 @@
|
||||
import chalk from 'chalk';
|
||||
import type Client from '../client';
|
||||
import type Client from '../../util/client';
|
||||
import type {
|
||||
Deployment,
|
||||
LastAliasRequest,
|
||||
PaginationOptions,
|
||||
Project,
|
||||
RollbackTarget,
|
||||
} from '@vercel-internals/types';
|
||||
import elapsed from '../output/elapsed';
|
||||
import formatDate from '../format-date';
|
||||
import getDeployment from '../get-deployment';
|
||||
import getScope from '../get-scope';
|
||||
import elapsed from '../../util/output/elapsed';
|
||||
import formatDate from '../../util/format-date';
|
||||
import getDeployment from '../../util/get-deployment';
|
||||
import { getPkgName } from '../../util/pkg-name';
|
||||
import getProjectByNameOrId from '../../util/projects/get-project-by-id-or-name';
|
||||
import getScope from '../../util/get-scope';
|
||||
import ms from 'ms';
|
||||
import renderAliasStatus from './render-alias-status';
|
||||
import sleep from '../sleep';
|
||||
import { ProjectNotFound } from '../../util/errors-ts';
|
||||
import renderAliasStatus from '../../util/alias/render-alias-status';
|
||||
import sleep from '../../util/sleep';
|
||||
|
||||
interface RollbackAlias {
|
||||
alias: {
|
||||
@@ -61,13 +64,6 @@ export default async function rollbackStatus({
|
||||
? 'Rollback in progress'
|
||||
: `Checking rollback status of ${project.name}`;
|
||||
|
||||
const check = async () => {
|
||||
const { lastRollbackTarget } = await client.fetch<any>(
|
||||
`/v9/projects/${project.id}?rollbackInfo=true`
|
||||
);
|
||||
return lastRollbackTarget;
|
||||
};
|
||||
|
||||
if (!contextName) {
|
||||
({ contextName } = await getScope(client));
|
||||
}
|
||||
@@ -78,8 +74,22 @@ export default async function rollbackStatus({
|
||||
// continuously loop until the rollback has explicitly succeeded, failed,
|
||||
// or timed out
|
||||
for (;;) {
|
||||
const { jobStatus, requestedAt, toDeploymentId }: RollbackTarget =
|
||||
(await check()) ?? {};
|
||||
const projectCheck = await getProjectByNameOrId(
|
||||
client,
|
||||
project.id,
|
||||
project.accountId,
|
||||
true
|
||||
);
|
||||
if (projectCheck instanceof ProjectNotFound) {
|
||||
throw projectCheck;
|
||||
}
|
||||
|
||||
const {
|
||||
jobStatus,
|
||||
requestedAt,
|
||||
toDeploymentId,
|
||||
type,
|
||||
}: Partial<LastAliasRequest> = projectCheck.lastAliasRequest ?? {};
|
||||
|
||||
if (
|
||||
!jobStatus ||
|
||||
@@ -89,12 +99,17 @@ export default async function rollbackStatus({
|
||||
output.log(`${spinnerMessage}…`);
|
||||
}
|
||||
|
||||
if (!jobStatus || requestedAt < recentThreshold) {
|
||||
if (
|
||||
!jobStatus ||
|
||||
!requestedAt ||
|
||||
!toDeploymentId ||
|
||||
requestedAt < recentThreshold
|
||||
) {
|
||||
output.log('No deployment rollback in progress');
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (jobStatus === 'skipped') {
|
||||
if (jobStatus === 'skipped' && type === 'rollback') {
|
||||
output.log('Rollback was skipped');
|
||||
return 0;
|
||||
}
|
||||
@@ -131,7 +146,7 @@ export default async function rollbackStatus({
|
||||
if (requestedAt < recentThreshold || Date.now() >= rollbackTimeout) {
|
||||
output.log(
|
||||
`The rollback exceeded its deadline - rerun ${chalk.bold(
|
||||
`vercel rollback ${toDeploymentId}`
|
||||
`${getPkgName()} rollback ${toDeploymentId}`
|
||||
)} to try again`
|
||||
);
|
||||
return 1;
|
||||
@@ -144,12 +144,6 @@ const main = async () => {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let cwd = argv['--cwd'];
|
||||
if (cwd) {
|
||||
process.chdir(cwd);
|
||||
}
|
||||
cwd = process.cwd();
|
||||
|
||||
// The second argument to the command can be:
|
||||
//
|
||||
// * a path to deploy (as in: `vercel path/`)
|
||||
@@ -277,6 +271,12 @@ const main = async () => {
|
||||
argv: process.argv,
|
||||
});
|
||||
|
||||
// The `--cwd` flag is respected for all sub-commands
|
||||
if (argv['--cwd']) {
|
||||
client.cwd = argv['--cwd'];
|
||||
}
|
||||
const { cwd } = client;
|
||||
|
||||
// Gets populated to the subcommand name when a built-in is
|
||||
// provided, otherwise it remains undefined for an extension
|
||||
let subcommand: string | undefined = undefined;
|
||||
@@ -571,6 +571,9 @@ const main = async () => {
|
||||
case 'project':
|
||||
func = require('./commands/project').default;
|
||||
break;
|
||||
case 'promote':
|
||||
func = require('./commands/promote').default;
|
||||
break;
|
||||
case 'pull':
|
||||
func = require('./commands/pull').default;
|
||||
break;
|
||||
|
||||
@@ -207,4 +207,12 @@ export default class Client extends EventEmitter implements Stdio {
|
||||
output: this.stderr as NodeJS.WriteStream,
|
||||
});
|
||||
}
|
||||
|
||||
get cwd(): string {
|
||||
return process.cwd();
|
||||
}
|
||||
|
||||
set cwd(v: string) {
|
||||
process.chdir(v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,21 +12,21 @@ export default async function createDeploy(
|
||||
client: Client,
|
||||
now: Now,
|
||||
contextName: string,
|
||||
paths: string[],
|
||||
path: string,
|
||||
createArgs: CreateOptions,
|
||||
org: Org,
|
||||
isSettingUpProject: boolean,
|
||||
cwd?: string,
|
||||
cwd: string,
|
||||
archive?: ArchiveFormat
|
||||
): Promise<any | DeploymentError> {
|
||||
try {
|
||||
return await now.create(
|
||||
paths,
|
||||
path,
|
||||
createArgs,
|
||||
org,
|
||||
isSettingUpProject,
|
||||
archive,
|
||||
cwd
|
||||
cwd,
|
||||
archive
|
||||
);
|
||||
} catch (err: unknown) {
|
||||
if (ERRORS_TS.isAPIError(err)) {
|
||||
@@ -109,10 +109,11 @@ export default async function createDeploy(
|
||||
client,
|
||||
now,
|
||||
contextName,
|
||||
paths,
|
||||
path,
|
||||
createArgs,
|
||||
org,
|
||||
isSettingUpProject
|
||||
isSettingUpProject,
|
||||
cwd
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,8 +40,7 @@ export default async function processDeployment({
|
||||
...args
|
||||
}: {
|
||||
now: Now;
|
||||
output: Output;
|
||||
paths: string[];
|
||||
path: string;
|
||||
requestBody: DeploymentOptions;
|
||||
uploadStamp: () => string;
|
||||
deployStamp: () => string;
|
||||
@@ -54,15 +53,14 @@ export default async function processDeployment({
|
||||
isSettingUpProject: boolean;
|
||||
archive?: ArchiveFormat;
|
||||
skipAutoDetectionConfirmation?: boolean;
|
||||
cwd?: string;
|
||||
cwd: string;
|
||||
rootDirectory?: string | null;
|
||||
noWait?: boolean;
|
||||
agent?: Agent;
|
||||
}) {
|
||||
let {
|
||||
now,
|
||||
output,
|
||||
paths,
|
||||
path,
|
||||
requestBody,
|
||||
deployStamp,
|
||||
force,
|
||||
@@ -72,10 +70,9 @@ export default async function processDeployment({
|
||||
rootDirectory,
|
||||
} = args;
|
||||
|
||||
const { debug } = output;
|
||||
|
||||
const client = now._client;
|
||||
const { output } = client;
|
||||
const { env = {} } = requestBody;
|
||||
|
||||
const token = now._token;
|
||||
if (!token) {
|
||||
throw new Error('Missing authentication token');
|
||||
@@ -87,7 +84,7 @@ export default async function processDeployment({
|
||||
token,
|
||||
debug: now._debug,
|
||||
userAgent: ua,
|
||||
path: paths[0],
|
||||
path,
|
||||
force,
|
||||
withCache,
|
||||
prebuilt,
|
||||
@@ -114,7 +111,7 @@ export default async function processDeployment({
|
||||
|
||||
if (event.type === 'file-count') {
|
||||
const { total, missing, uploads } = event.payload;
|
||||
debug(`Total files ${total.size}, ${missing.length} changed`);
|
||||
output.debug(`Total files ${total.size}, ${missing.length} changed`);
|
||||
|
||||
const missingSize = missing
|
||||
.map((sha: string) => total.get(sha).data.length)
|
||||
@@ -157,7 +154,7 @@ export default async function processDeployment({
|
||||
}
|
||||
|
||||
if (event.type === 'file-uploaded') {
|
||||
debug(
|
||||
output.debug(
|
||||
`Uploaded: ${event.payload.file.names.join(' ')} (${bytes(
|
||||
event.payload.file.data.length
|
||||
)})`
|
||||
@@ -166,8 +163,8 @@ export default async function processDeployment({
|
||||
|
||||
if (event.type === 'created') {
|
||||
await linkFolderToProject(
|
||||
output,
|
||||
cwd || paths[0],
|
||||
client,
|
||||
cwd,
|
||||
{
|
||||
orgId: org.id,
|
||||
projectId: event.payload.projectId,
|
||||
|
||||
@@ -107,7 +107,7 @@ export default class Now extends EventEmitter {
|
||||
}
|
||||
|
||||
async create(
|
||||
paths: string[],
|
||||
path: string,
|
||||
{
|
||||
// Legacy
|
||||
nowConfig: nowConfig = {},
|
||||
@@ -135,8 +135,8 @@ export default class Now extends EventEmitter {
|
||||
}: CreateOptions,
|
||||
org: Org,
|
||||
isSettingUpProject: boolean,
|
||||
archive?: ArchiveFormat,
|
||||
cwd?: string
|
||||
cwd: string,
|
||||
archive?: ArchiveFormat
|
||||
) {
|
||||
let hashes: any = {};
|
||||
const uploadStamp = stamp();
|
||||
@@ -163,9 +163,8 @@ export default class Now extends EventEmitter {
|
||||
|
||||
const deployment = await processDeployment({
|
||||
now: this,
|
||||
output: this._output,
|
||||
agent: this._client.agent,
|
||||
paths,
|
||||
path,
|
||||
requestBody,
|
||||
uploadStamp,
|
||||
deployStamp,
|
||||
|
||||
@@ -220,8 +220,20 @@ export async function findRepoRoot(start: string): Promise<string | undefined> {
|
||||
break;
|
||||
}
|
||||
|
||||
// if `.vercel/repo.json` exists (already linked),
|
||||
// then consider this the repo root
|
||||
const repoConfigPath = join(current, VERCEL_DIR, VERCEL_DIR_REPO);
|
||||
let stat = await lstat(repoConfigPath).catch(err => {
|
||||
if (err.code !== 'ENOENT') throw err;
|
||||
});
|
||||
if (stat) {
|
||||
return current;
|
||||
}
|
||||
|
||||
// if `.git/config` exists (unlinked),
|
||||
// then consider this the repo root
|
||||
const gitConfigPath = join(current, '.git/config');
|
||||
const stat = await lstat(gitConfigPath).catch(err => {
|
||||
stat = await lstat(gitConfigPath).catch(err => {
|
||||
if (err.code !== 'ENOENT') throw err;
|
||||
});
|
||||
if (stat) {
|
||||
@@ -247,19 +259,19 @@ function sortByDirectory(a: RepoProjectConfig, b: RepoProjectConfig): number {
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the matching Project from an array of Project links
|
||||
* Finds the matching Projects from an array of Project links
|
||||
* where the provided relative path is within the Project's
|
||||
* root directory.
|
||||
*/
|
||||
export function findProjectFromPath(
|
||||
export function findProjectsFromPath(
|
||||
projects: RepoProjectConfig[],
|
||||
path: string
|
||||
): RepoProjectConfig | undefined {
|
||||
): RepoProjectConfig[] {
|
||||
const normalizedPath = normalizePath(path);
|
||||
return projects
|
||||
.slice()
|
||||
.sort(sortByDirectory)
|
||||
.find(project => {
|
||||
.filter(project => {
|
||||
if (project.directory === '.') {
|
||||
// Project has no "Root Directory" setting, so any path is valid
|
||||
return true;
|
||||
@@ -270,3 +282,13 @@ export function findProjectFromPath(
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: remove
|
||||
*/
|
||||
export function findProjectFromPath(
|
||||
projects: RepoProjectConfig[],
|
||||
path: string
|
||||
): RepoProjectConfig | undefined {
|
||||
return findProjectsFromPath(projects, path)[0];
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ export default async function setupAndLink(
|
||||
const project = projectOrNewProjectName;
|
||||
|
||||
await linkFolderToProject(
|
||||
output,
|
||||
client,
|
||||
path,
|
||||
{
|
||||
projectId: project.id,
|
||||
@@ -207,7 +207,7 @@ export default async function setupAndLink(
|
||||
client,
|
||||
now,
|
||||
config.currentTeam || 'current user',
|
||||
[sourcePath],
|
||||
sourcePath,
|
||||
createArgs,
|
||||
org,
|
||||
true,
|
||||
@@ -251,7 +251,7 @@ export default async function setupAndLink(
|
||||
Object.assign(project, settings);
|
||||
|
||||
await linkFolderToProject(
|
||||
output,
|
||||
client,
|
||||
path,
|
||||
{
|
||||
projectId: project.id,
|
||||
|
||||
40
packages/cli/src/util/projects/get-project-by-cwd-or-link.ts
Normal file
40
packages/cli/src/util/projects/get-project-by-cwd-or-link.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type Client from '../client';
|
||||
import { ProjectNotFound } from '../errors-ts';
|
||||
import { ensureLink } from '../link/ensure-link';
|
||||
import getProjectByNameOrId from './get-project-by-id-or-name';
|
||||
import type { Project } from '@vercel-internals/types';
|
||||
|
||||
export default async function getProjectByCwdOrLink({
|
||||
autoConfirm,
|
||||
client,
|
||||
commandName,
|
||||
cwd,
|
||||
projectNameOrId,
|
||||
}: {
|
||||
autoConfirm?: boolean;
|
||||
client: Client;
|
||||
commandName: string;
|
||||
cwd: string;
|
||||
projectNameOrId?: string;
|
||||
}): Promise<Project> {
|
||||
if (projectNameOrId) {
|
||||
const project = await getProjectByNameOrId(client, projectNameOrId);
|
||||
if (project instanceof ProjectNotFound) {
|
||||
throw project;
|
||||
}
|
||||
return project;
|
||||
}
|
||||
|
||||
// ensure the current directory is a linked project
|
||||
const linkedProject = await ensureLink(commandName, client, cwd, {
|
||||
autoConfirm,
|
||||
});
|
||||
|
||||
if (typeof linkedProject === 'number') {
|
||||
const err: NodeJS.ErrnoException = new Error('Link project error');
|
||||
err.code = 'ERR_LINK_PROJECT';
|
||||
throw err;
|
||||
}
|
||||
|
||||
return linkedProject.project;
|
||||
}
|
||||
104
packages/cli/src/util/projects/get-project-by-deployment.ts
Normal file
104
packages/cli/src/util/projects/get-project-by-deployment.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import chalk from 'chalk';
|
||||
import type Client from '../client';
|
||||
import type { Deployment, Project, Team } from '@vercel-internals/types';
|
||||
import getDeployment from '../get-deployment';
|
||||
import getProjectByNameOrId from './get-project-by-id-or-name';
|
||||
import getScope from '../get-scope';
|
||||
import getTeamById from '../teams/get-team-by-id';
|
||||
import { isValidName } from '../is-valid-name';
|
||||
import { Output } from '../output';
|
||||
import { ProjectNotFound } from '../errors-ts';
|
||||
|
||||
export default async function getProjectByDeployment({
|
||||
client,
|
||||
deployId,
|
||||
output,
|
||||
}: {
|
||||
client: Client;
|
||||
deployId: string;
|
||||
output?: Output;
|
||||
}): Promise<{
|
||||
contextName: string;
|
||||
deployment: Deployment;
|
||||
project: Project;
|
||||
}> {
|
||||
const { config } = client;
|
||||
const { contextName } = await getScope(client);
|
||||
|
||||
if (!isValidName(deployId)) {
|
||||
throw new Error(
|
||||
`The provided argument "${deployId}" is not a valid deployment ID or URL`
|
||||
);
|
||||
}
|
||||
|
||||
let deployment: Deployment;
|
||||
let team: Team | undefined;
|
||||
|
||||
try {
|
||||
output?.spinner(
|
||||
`Fetching deployment "${deployId}" in ${chalk.bold(contextName)}…`
|
||||
);
|
||||
|
||||
const [teamResult, deploymentResult] = await Promise.allSettled([
|
||||
config.currentTeam ? getTeamById(client, config.currentTeam) : undefined,
|
||||
getDeployment(client, contextName, deployId),
|
||||
]);
|
||||
|
||||
if (teamResult.status === 'rejected') {
|
||||
throw new Error(
|
||||
`Failed to retrieve team information: ${teamResult.reason}`
|
||||
);
|
||||
}
|
||||
|
||||
if (deploymentResult.status === 'rejected') {
|
||||
throw new Error(deploymentResult.reason);
|
||||
}
|
||||
|
||||
team = teamResult.value;
|
||||
deployment = deploymentResult.value;
|
||||
|
||||
// re-render the spinner text
|
||||
output?.log(
|
||||
`Fetching deployment "${deployId}" in ${chalk.bold(contextName)}…`
|
||||
);
|
||||
|
||||
if (deployment.team?.id) {
|
||||
if (!team || deployment.team.id !== team.id) {
|
||||
const err: NodeJS.ErrnoException = new Error(
|
||||
team
|
||||
? `Deployment doesn't belong to current team ${chalk.bold(
|
||||
contextName
|
||||
)}`
|
||||
: `Deployment belongs to a different team`
|
||||
);
|
||||
err.code = 'ERR_INVALID_TEAM';
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
if (team) {
|
||||
const err: NodeJS.ErrnoException = new Error(
|
||||
`Deployment doesn't belong to current team ${chalk.bold(contextName)}`
|
||||
);
|
||||
err.code = 'ERR_INVALID_TEAM';
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!deployment.projectId) {
|
||||
throw new Error('Deployment is not associated to a project');
|
||||
}
|
||||
|
||||
const project = await getProjectByNameOrId(client, deployment.projectId);
|
||||
if (project instanceof ProjectNotFound) {
|
||||
throw project;
|
||||
}
|
||||
|
||||
return {
|
||||
contextName,
|
||||
deployment,
|
||||
project,
|
||||
};
|
||||
} finally {
|
||||
output?.stopSpinner();
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,13 @@ import { isAPIError, ProjectNotFound } from '../errors-ts';
|
||||
export default async function getProjectByNameOrId(
|
||||
client: Client,
|
||||
projectNameOrId: string,
|
||||
accountId?: string
|
||||
accountId?: string,
|
||||
includeRollbackInfo?: boolean
|
||||
) {
|
||||
try {
|
||||
const qs = includeRollbackInfo ? '?rollbackInfo=true' : '';
|
||||
const project = await client.fetch<Project>(
|
||||
`/v8/projects/${encodeURIComponent(projectNameOrId)}`,
|
||||
`/v9/projects/${encodeURIComponent(projectNameOrId)}${qs}`,
|
||||
{ accountId }
|
||||
);
|
||||
return project;
|
||||
|
||||
@@ -2,7 +2,7 @@ import fs from 'fs';
|
||||
import AJV from 'ajv';
|
||||
import chalk from 'chalk';
|
||||
import { join, relative } from 'path';
|
||||
import { ensureDir, readJSON } from 'fs-extra';
|
||||
import { ensureDir } from 'fs-extra';
|
||||
import { promisify } from 'util';
|
||||
|
||||
import getProjectByIdOrName from '../projects/get-project-by-id-or-name';
|
||||
@@ -10,7 +10,6 @@ import Client from '../client';
|
||||
import { InvalidToken, isAPIError, ProjectNotFound } from '../errors-ts';
|
||||
import getUser from '../get-user';
|
||||
import getTeamById from '../teams/get-team-by-id';
|
||||
import { Output } from '../output';
|
||||
import type {
|
||||
Project,
|
||||
ProjectLinkResult,
|
||||
@@ -22,8 +21,9 @@ import { isDirectory } from '../config/global-path';
|
||||
import { NowBuildError, getPlatformEnv } from '@vercel/build-utils';
|
||||
import outputCode from '../output/code';
|
||||
import { isErrnoException, isError } from '@vercel/error-utils';
|
||||
import { findProjectFromPath, findRepoRoot } from '../link/repo';
|
||||
import { findProjectsFromPath, getRepoLink } from '../link/repo';
|
||||
import { addToGitIgnore } from '../link/add-to-gitignore';
|
||||
import type { RepoProjectConfig } from '../link/repo';
|
||||
|
||||
const readFile = promisify(fs.readFile);
|
||||
const writeFile = promisify(fs.writeFile);
|
||||
@@ -69,38 +69,49 @@ export function getVercelDirectory(cwd: string): string {
|
||||
return existingDirs[0] || possibleDirs[0];
|
||||
}
|
||||
|
||||
async function getLink(path: string): Promise<ProjectLink | null> {
|
||||
// Try the individual project linking style (`${cwd}/.vercel/project.json`)
|
||||
const dir = getVercelDirectory(path);
|
||||
const linkFromProject = await getLinkFromDir(dir);
|
||||
if (linkFromProject) {
|
||||
return linkFromProject;
|
||||
}
|
||||
|
||||
// Try the repo linking style (`${repoRoot}/.vercel/repo.json`)
|
||||
return getLinkFromRepo(path);
|
||||
async function getProjectLink(
|
||||
client: Client,
|
||||
path: string
|
||||
): Promise<ProjectLink | null> {
|
||||
return (
|
||||
(await getProjectLinkFromRepoLink(client, path)) ||
|
||||
(await getLinkFromDir(getVercelDirectory(path)))
|
||||
);
|
||||
}
|
||||
|
||||
async function getLinkFromRepo(path: string): Promise<ProjectLink | null> {
|
||||
const repoRoot = await findRepoRoot(path);
|
||||
if (!repoRoot) {
|
||||
async function getProjectLinkFromRepoLink(
|
||||
client: Client,
|
||||
path: string
|
||||
): Promise<ProjectLink | null> {
|
||||
const repoLink = await getRepoLink(path);
|
||||
if (!repoLink?.repoConfig) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const vercelDir = join(repoRoot, VERCEL_DIR);
|
||||
const repoJsonPath = join(vercelDir, VERCEL_DIR_REPO);
|
||||
const repoJson = await readJSON(repoJsonPath);
|
||||
const project = findProjectFromPath(
|
||||
repoJson.projects,
|
||||
relative(repoRoot, path)
|
||||
);
|
||||
if (project) {
|
||||
return { orgId: repoJson.orgId, projectId: project.id };
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (!isErrnoException(err) || err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
const projects = findProjectsFromPath(
|
||||
repoLink.repoConfig.projects,
|
||||
relative(repoLink.rootPath, path)
|
||||
);
|
||||
let project: RepoProjectConfig | undefined;
|
||||
if (projects.length === 1) {
|
||||
project = projects[0];
|
||||
} else {
|
||||
const { p } = await client.prompt({
|
||||
name: 'p',
|
||||
type: 'list',
|
||||
message: `Please select a Project:`,
|
||||
choices: repoLink.repoConfig.projects.map(p => ({
|
||||
value: p,
|
||||
name: p.name,
|
||||
})),
|
||||
});
|
||||
project = p;
|
||||
}
|
||||
if (project) {
|
||||
return {
|
||||
orgId: repoLink.repoConfig.orgId,
|
||||
projectId: project.id,
|
||||
repoRoot: repoLink.rootPath,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -154,9 +165,45 @@ async function getOrgById(client: Client, orgId: string): Promise<Org | null> {
|
||||
return { type: 'user', id: orgId, slug: user.username };
|
||||
}
|
||||
|
||||
async function hasProjectLink(
|
||||
projectLink: ProjectLink,
|
||||
path: string
|
||||
): Promise<boolean> {
|
||||
// "linked" via env vars?
|
||||
const VERCEL_ORG_ID = getPlatformEnv('ORG_ID');
|
||||
const VERCEL_PROJECT_ID = getPlatformEnv('PROJECT_ID');
|
||||
if (
|
||||
VERCEL_ORG_ID === projectLink.orgId &&
|
||||
VERCEL_PROJECT_ID === projectLink.projectId
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// linked via `repo.json`?
|
||||
const repoLink = await getRepoLink(path);
|
||||
if (
|
||||
repoLink?.repoConfig?.orgId === projectLink.orgId &&
|
||||
repoLink.repoConfig.projects.find(p => p.id === projectLink.projectId)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// if the project is already linked, we skip linking
|
||||
const link = await getLinkFromDir(getVercelDirectory(path));
|
||||
if (
|
||||
link &&
|
||||
link.orgId === projectLink.orgId &&
|
||||
link.projectId === projectLink.projectId
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function getLinkedProject(
|
||||
client: Client,
|
||||
path = process.cwd()
|
||||
path = client.cwd
|
||||
): Promise<ProjectLinkResult> {
|
||||
const { output } = client;
|
||||
const VERCEL_ORG_ID = getPlatformEnv('ORG_ID');
|
||||
@@ -177,7 +224,7 @@ export async function getLinkedProject(
|
||||
const link =
|
||||
VERCEL_ORG_ID && VERCEL_PROJECT_ID
|
||||
? { orgId: VERCEL_ORG_ID, projectId: VERCEL_PROJECT_ID }
|
||||
: await getLink(path);
|
||||
: await getProjectLink(client, path);
|
||||
|
||||
if (!link) {
|
||||
return { status: 'not_linked', org: null, project: null };
|
||||
@@ -234,32 +281,19 @@ export async function getLinkedProject(
|
||||
return { status: 'not_linked', org: null, project: null };
|
||||
}
|
||||
|
||||
return { status: 'linked', org, project };
|
||||
return { status: 'linked', org, project, repoRoot: link.repoRoot };
|
||||
}
|
||||
|
||||
export async function linkFolderToProject(
|
||||
output: Output,
|
||||
client: Client,
|
||||
path: string,
|
||||
projectLink: ProjectLink,
|
||||
projectName: string,
|
||||
orgSlug: string,
|
||||
successEmoji: EmojiLabel = 'link'
|
||||
) {
|
||||
const VERCEL_ORG_ID = getPlatformEnv('ORG_ID');
|
||||
const VERCEL_PROJECT_ID = getPlatformEnv('PROJECT_ID');
|
||||
|
||||
// if defined, skip linking
|
||||
if (VERCEL_ORG_ID || VERCEL_PROJECT_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if the project is already linked, we skip linking
|
||||
const link = await getLink(path);
|
||||
if (
|
||||
link &&
|
||||
link.orgId === projectLink.orgId &&
|
||||
link.projectId === projectLink.projectId
|
||||
) {
|
||||
if (await hasProjectLink(projectLink, path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -287,7 +321,7 @@ export async function linkFolderToProject(
|
||||
// update .gitignore
|
||||
const isGitIgnoreUpdated = await addToGitIgnore(path);
|
||||
|
||||
output.print(
|
||||
client.output.print(
|
||||
prependEmoji(
|
||||
`Linked to ${chalk.bold(
|
||||
`${orgSlug}/${projectName}`
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
import chalk from 'chalk';
|
||||
import type Client from '../client';
|
||||
import type { Project } from '@vercel-internals/types';
|
||||
import { getCommandName } from '../pkg-name';
|
||||
import { getDeploymentByIdOrURL } from '../deploy/get-deployment-by-id-or-url';
|
||||
import getScope from '../get-scope';
|
||||
import { isErrnoException } from '@vercel/error-utils';
|
||||
import ms from 'ms';
|
||||
import rollbackStatus from './status';
|
||||
|
||||
/**
|
||||
* Requests a rollback and waits for it complete.
|
||||
* @param {Client} client - The Vercel client instance
|
||||
* @param {string} deployIdOrUrl - The deployment name or id to rollback
|
||||
* @param {Project} project - Project info instance
|
||||
* @param {string} [timeout] - Time to poll for succeeded/failed state
|
||||
* @returns {Promise<number>} Resolves an exit code; 0 on success
|
||||
*/
|
||||
export default async function requestRollback({
|
||||
client,
|
||||
deployIdOrUrl,
|
||||
project,
|
||||
timeout,
|
||||
}: {
|
||||
client: Client;
|
||||
deployIdOrUrl: string;
|
||||
project: Project;
|
||||
timeout?: string;
|
||||
}): Promise<number> {
|
||||
const { output } = client;
|
||||
const { contextName } = await getScope(client);
|
||||
|
||||
try {
|
||||
const deployment = await getDeploymentByIdOrURL({
|
||||
client,
|
||||
contextName,
|
||||
deployIdOrUrl,
|
||||
});
|
||||
|
||||
// create the rollback
|
||||
await client.fetch(`/v9/projects/${project.id}/rollback/${deployment.id}`, {
|
||||
body: {}, // required
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
if (timeout !== undefined && ms(timeout) === 0) {
|
||||
output.log(
|
||||
`Successfully requested rollback of ${chalk.bold(project.name)} to ${
|
||||
deployment.url
|
||||
} (${deployment.id})`
|
||||
);
|
||||
output.log(
|
||||
`To check rollback status, run ${getCommandName('rollback')}.`
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check the status
|
||||
return await rollbackStatus({
|
||||
client,
|
||||
contextName,
|
||||
deployment,
|
||||
project,
|
||||
timeout,
|
||||
});
|
||||
} catch (err: unknown) {
|
||||
output.prettyError(err);
|
||||
if (isErrnoException(err) && err.code === 'ERR_INVALID_TEAM') {
|
||||
output.error(
|
||||
`Use ${chalk.bold('vc switch')} to change your current team`
|
||||
);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { lstat as lstatRaw } from 'fs';
|
||||
import { promisify } from 'util';
|
||||
import { lstat } from 'fs-extra';
|
||||
import { Output } from './output';
|
||||
import chalk from 'chalk';
|
||||
import { homedir } from 'os';
|
||||
@@ -7,8 +6,6 @@ import confirm from './input/confirm';
|
||||
import toHumanPath from './humanize-path';
|
||||
import Client from './client';
|
||||
|
||||
const stat = promisify(lstatRaw);
|
||||
|
||||
/**
|
||||
* A helper function to validate the `rootDirectory` input.
|
||||
*/
|
||||
@@ -18,7 +15,7 @@ export async function validateRootDirectory(
|
||||
path: string,
|
||||
errorSuffix: string
|
||||
) {
|
||||
const pathStat = await stat(path).catch(() => null);
|
||||
const pathStat = await lstat(path).catch(() => null);
|
||||
const suffix = errorSuffix ? ` ${errorSuffix}` : '';
|
||||
|
||||
if (!pathStat) {
|
||||
@@ -66,7 +63,7 @@ export default async function validatePaths(
|
||||
const path = paths[0];
|
||||
|
||||
// can only deploy a directory
|
||||
const pathStat = await stat(path).catch(() => null);
|
||||
const pathStat = await lstat(path).catch(() => null);
|
||||
|
||||
if (!pathStat) {
|
||||
output.error(`Could not find ${chalk.cyan(`“${toHumanPath(path)}”`)}`);
|
||||
|
||||
1
packages/cli/test/fixtures/unit/commands/deploy/monorepo-static/.gitignore
vendored
Normal file
1
packages/cli/test/fixtures/unit/commands/deploy/monorepo-static/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!.vercel
|
||||
11
packages/cli/test/fixtures/unit/commands/deploy/monorepo-static/.vercel/repo.json
vendored
Normal file
11
packages/cli/test/fixtures/unit/commands/deploy/monorepo-static/.vercel/repo.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"orgId": "team_dummy",
|
||||
"remoteName": "origin",
|
||||
"projects": [
|
||||
{
|
||||
"id": "QmbKpqpiUqbcke",
|
||||
"name": "app",
|
||||
"directory": "app"
|
||||
}
|
||||
]
|
||||
}
|
||||
1
packages/cli/test/fixtures/unit/commands/deploy/monorepo-static/app/index.html
vendored
Normal file
1
packages/cli/test/fixtures/unit/commands/deploy/monorepo-static/app/index.html
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<h1>hi</h1>
|
||||
3
packages/cli/test/fixtures/unit/commands/deploy/monorepo-static/app/style.css
vendored
Normal file
3
packages/cli/test/fixtures/unit/commands/deploy/monorepo-static/app/style.css
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
body {
|
||||
background-color: red;
|
||||
}
|
||||
4
packages/cli/test/fixtures/unit/commands/promote/simple-next-site/.vercel/project.json
vendored
Normal file
4
packages/cli/test/fixtures/unit/commands/promote/simple-next-site/.vercel/project.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"orgId": "team_dummy",
|
||||
"projectId": "vercel-promote"
|
||||
}
|
||||
12
packages/cli/test/fixtures/unit/commands/promote/simple-next-site/package.json
vendored
Normal file
12
packages/cli/test/fixtures/unit/commands/promote/simple-next-site/package.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"scripts": {
|
||||
"build": "next build",
|
||||
"dev": "next",
|
||||
"now-build": "next build"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "13.4.3",
|
||||
"react": "latest",
|
||||
"react-dom": "latest"
|
||||
}
|
||||
}
|
||||
10
packages/cli/test/fixtures/unit/commands/promote/simple-next-site/vercel.json
vendored
Normal file
10
packages/cli/test/fixtures/unit/commands/promote/simple-next-site/vercel.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "vercel-promote",
|
||||
"routes": [
|
||||
{
|
||||
"src": "/(.*)",
|
||||
"dest": "/index?route-param=b"
|
||||
}
|
||||
]
|
||||
}
|
||||
3
packages/cli/test/fixtures/unit/commands/rollback/simple-next-site/.gitignore
vendored
Normal file
3
packages/cli/test/fixtures/unit/commands/rollback/simple-next-site/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.next
|
||||
yarn.lock
|
||||
!.vercel
|
||||
11
packages/cli/test/fixtures/unit/commands/rollback/simple-next-site/pages/index.js
vendored
Normal file
11
packages/cli/test/fixtures/unit/commands/rollback/simple-next-site/pages/index.js
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { withRouter } from 'next/router';
|
||||
|
||||
function Index({ router }) {
|
||||
const data = {
|
||||
pathname: router.pathname,
|
||||
query: router.query,
|
||||
};
|
||||
return <div>{JSON.stringify(data)}</div>;
|
||||
}
|
||||
|
||||
export default withRouter(Index);
|
||||
1
packages/cli/test/fixtures/unit/monorepo-link/.gitignore
vendored
Normal file
1
packages/cli/test/fixtures/unit/monorepo-link/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!.vercel
|
||||
21
packages/cli/test/fixtures/unit/monorepo-link/.vercel/repo.json
vendored
Normal file
21
packages/cli/test/fixtures/unit/monorepo-link/.vercel/repo.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"orgId": "team_dummy",
|
||||
"remoteName": "origin",
|
||||
"projects": [
|
||||
{
|
||||
"id": "QmbKpqpiUqbcke",
|
||||
"name": "monorepo-dashboard",
|
||||
"directory": "dashboard"
|
||||
},
|
||||
{
|
||||
"id": "QmX6P93ChNDoZP",
|
||||
"name": "monorepo-marketing",
|
||||
"directory": "marketing"
|
||||
},
|
||||
{
|
||||
"id": "QmScb7GPQt6gsS",
|
||||
"name": "monorepo-blog",
|
||||
"directory": "blog"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -34,16 +34,19 @@ export function setupUnitFixture(fixtureName: string) {
|
||||
);
|
||||
}
|
||||
|
||||
const cwd = setupTmpDir(fixtureName);
|
||||
fs.copySync(fixturePath, cwd);
|
||||
return cwd;
|
||||
}
|
||||
|
||||
export function setupTmpDir(fixtureName?: string) {
|
||||
if (!tempRoot) {
|
||||
// Create a shared root temp directory for fixture files
|
||||
tempRoot = tmp.dirSync({ unsafeCleanup: true }); // clean up even if files are left
|
||||
}
|
||||
|
||||
const cwd = path.join(tempRoot.name, String(tempNumber++), fixtureName);
|
||||
|
||||
const cwd = path.join(tempRoot.name, String(tempNumber++), fixtureName ?? '');
|
||||
fs.mkdirpSync(cwd);
|
||||
fs.copySync(fixturePath, cwd);
|
||||
|
||||
return cwd;
|
||||
}
|
||||
|
||||
|
||||
15
packages/cli/test/integration-3.test.ts
vendored
15
packages/cli/test/integration-3.test.ts
vendored
@@ -12,6 +12,7 @@ import retry from 'async-retry';
|
||||
import fs, { ensureDir } from 'fs-extra';
|
||||
import logo from '../src/util/output/logo';
|
||||
import sleep from '../src/util/sleep';
|
||||
import humanizePath from '../src/util/humanize-path';
|
||||
import pkg from '../package.json';
|
||||
import { fetchTokenWithRetry } from '../../../test/lib/deployment/now-deploy';
|
||||
import waitForPrompt from './helpers/wait-for-prompt';
|
||||
@@ -819,7 +820,9 @@ test('create a production deployment', async () => {
|
||||
});
|
||||
|
||||
test('try to deploy non-existing path', async () => {
|
||||
const goal = `Error: The specified file or directory "${session}" does not exist.`;
|
||||
const goal = `Error: Could not find “${humanizePath(
|
||||
path.join(process.cwd(), session)
|
||||
)}”`;
|
||||
|
||||
const { stderr, stdout, exitCode } = await execCli(binaryPath, [
|
||||
session,
|
||||
@@ -1201,9 +1204,13 @@ test('vercel hasOwnProperty not a valid subcommand', async () => {
|
||||
const output = await execCli(binaryPath, ['hasOwnProperty']);
|
||||
|
||||
expect(output.exitCode, formatOutput(output)).toBe(1);
|
||||
expect(output.stderr).toMatch(
|
||||
/The specified file or directory "hasOwnProperty" does not exist/gm
|
||||
);
|
||||
expect(
|
||||
output.stderr.endsWith(
|
||||
`Error: Could not find “${humanizePath(
|
||||
path.join(process.cwd(), 'hasOwnProperty')
|
||||
)}”`
|
||||
)
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
test('create zero-config deployment', async () => {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const originalCwd = process.cwd();
|
||||
|
||||
// Register Jest matcher extensions for CLI unit tests
|
||||
import './matchers';
|
||||
|
||||
@@ -73,6 +75,8 @@ export class MockClient extends Client {
|
||||
});
|
||||
|
||||
this.scenario = Router();
|
||||
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
@@ -99,11 +103,14 @@ export class MockClient extends Client {
|
||||
};
|
||||
this.config = {};
|
||||
this.localConfig = {};
|
||||
this.localConfigPath = undefined;
|
||||
|
||||
this.scenario = Router();
|
||||
|
||||
this.agent?.destroy();
|
||||
this.agent = undefined;
|
||||
|
||||
this.cwd = originalCwd;
|
||||
}
|
||||
|
||||
async startMockServer() {
|
||||
@@ -156,7 +163,7 @@ beforeAll(async () => {
|
||||
await client.startMockServer();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
afterEach(() => {
|
||||
client.reset();
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import chance from 'chance';
|
||||
import { client } from './client';
|
||||
import { Build, Deployment, User } from '@vercel-internals/types';
|
||||
import type { Request, Response } from 'express';
|
||||
import { defaultProject } from './project';
|
||||
|
||||
let deployments = new Map<string, Deployment>();
|
||||
let deploymentBuilds = new Map<Deployment, Build[]>();
|
||||
@@ -16,6 +17,7 @@ export function useDeployment({
|
||||
creator,
|
||||
state = 'READY',
|
||||
createdAt,
|
||||
project = defaultProject,
|
||||
}: {
|
||||
creator: Pick<User, 'id' | 'email' | 'name' | 'username'>;
|
||||
state?:
|
||||
@@ -26,6 +28,7 @@ export function useDeployment({
|
||||
| 'READY'
|
||||
| 'CANCELED';
|
||||
createdAt?: number;
|
||||
project: any; // FIX ME: Use `Project` once PR #9956 is merged
|
||||
}) {
|
||||
setupDeploymentEndpoints();
|
||||
|
||||
@@ -53,6 +56,7 @@ export function useDeployment({
|
||||
name,
|
||||
ownerId: creator.id,
|
||||
plan: 'hobby',
|
||||
projectId: project.id,
|
||||
public: false,
|
||||
ready: createdAt + 30000,
|
||||
readyState: state,
|
||||
|
||||
@@ -56,7 +56,7 @@ export async function toOutput(
|
||||
resolve({
|
||||
pass: false,
|
||||
message() {
|
||||
return `${hint}Timed out waiting ${timeout} ms for output`;
|
||||
return `${hint}Timed out waiting ${timeout} ms for output.\n\nExpected: "${test}"\nReceived: "${output}"`;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ export const defaultProject: Project = {
|
||||
url: 'a-project-name-rjtr4pz3f.vercel.app',
|
||||
},
|
||||
],
|
||||
lastRollbackTarget: null,
|
||||
lastAliasRequest: null,
|
||||
alias: [
|
||||
{
|
||||
domain: 'foobar.com',
|
||||
@@ -205,10 +205,10 @@ export function useProject(
|
||||
project: Partial<Project> = defaultProject,
|
||||
projectEnvs: ProjectEnvVariable[] = envs
|
||||
) {
|
||||
client.scenario.get(`/v8/projects/${project.name}`, (_req, res) => {
|
||||
client.scenario.get(`/:version/projects/${project.name}`, (_req, res) => {
|
||||
res.json(project);
|
||||
});
|
||||
client.scenario.get(`/v8/projects/${project.id}`, (_req, res) => {
|
||||
client.scenario.get(`/:version/projects/${project.id}`, (_req, res) => {
|
||||
res.json(project);
|
||||
});
|
||||
client.scenario.patch(`/:version/projects/${project.id}`, (req, res) => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@ import { setupUnitFixture } from '../../helpers/setup-unit-fixture';
|
||||
import { defaultProject, useProject } from '../../mocks/project';
|
||||
import { useTeams } from '../../mocks/team';
|
||||
import { useUser } from '../../mocks/user';
|
||||
import humanizePath from '../../../src/util/humanize-path';
|
||||
|
||||
describe('deploy', () => {
|
||||
it('should reject deploying a single file', async () => {
|
||||
@@ -30,10 +31,13 @@ describe('deploy', () => {
|
||||
});
|
||||
|
||||
it('should reject deploying a directory that does not exist', async () => {
|
||||
client.setArgv('deploy', 'does-not-exists');
|
||||
const badName = 'does-not-exist';
|
||||
client.setArgv('deploy', badName);
|
||||
const exitCodePromise = deploy(client);
|
||||
await expect(client.stderr).toOutput(
|
||||
`Error: The specified file or directory "does-not-exists" does not exist.\n`
|
||||
`Error: Could not find “${humanizePath(
|
||||
join(client.cwd, 'does-not-exist')
|
||||
)}”\n`
|
||||
);
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
@@ -141,232 +145,250 @@ describe('deploy', () => {
|
||||
});
|
||||
|
||||
it('should send a tgz file when `--archive=tgz`', async () => {
|
||||
const cwd = setupUnitFixture('commands/deploy/static');
|
||||
const originalCwd = process.cwd();
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
name: 'static',
|
||||
id: 'static',
|
||||
});
|
||||
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
name: 'static',
|
||||
id: 'static',
|
||||
let body: any;
|
||||
client.scenario.post(`/v13/deployments`, (req, res) => {
|
||||
body = req.body;
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
});
|
||||
});
|
||||
client.scenario.get(`/v13/deployments/dpl_archive_test`, (req, res) => {
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
readyState: 'READY',
|
||||
aliasAssigned: true,
|
||||
alias: [],
|
||||
});
|
||||
});
|
||||
client.scenario.get(`/v10/now/deployments/dpl_archive_test`, (req, res) => {
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
readyState: 'READY',
|
||||
aliasAssigned: true,
|
||||
alias: [],
|
||||
});
|
||||
});
|
||||
|
||||
let body: any;
|
||||
client.scenario.post(`/v13/deployments`, (req, res) => {
|
||||
body = req.body;
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
});
|
||||
});
|
||||
client.scenario.get(`/v13/deployments/dpl_archive_test`, (req, res) => {
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
readyState: 'READY',
|
||||
aliasAssigned: true,
|
||||
alias: [],
|
||||
});
|
||||
});
|
||||
client.scenario.get(
|
||||
`/v10/now/deployments/dpl_archive_test`,
|
||||
(req, res) => {
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
readyState: 'READY',
|
||||
aliasAssigned: true,
|
||||
alias: [],
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
client.setArgv('deploy', '--archive=tgz');
|
||||
const exitCode = await deploy(client);
|
||||
expect(exitCode).toEqual(0);
|
||||
expect(body?.files?.length).toEqual(1);
|
||||
expect(body?.files?.[0].file).toEqual('.vercel/source.tgz');
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
client.cwd = setupUnitFixture('commands/deploy/static');
|
||||
client.setArgv('deploy', '--archive=tgz');
|
||||
const exitCode = await deploy(client);
|
||||
expect(exitCode).toEqual(0);
|
||||
expect(body?.files?.length).toEqual(1);
|
||||
expect(body?.files?.[0].file).toEqual('.vercel/source.tgz');
|
||||
});
|
||||
|
||||
it('should pass flag to skip custom domain assignment', async () => {
|
||||
const cwd = setupUnitFixture('commands/deploy/static');
|
||||
const originalCwd = process.cwd();
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
name: 'static',
|
||||
id: 'static',
|
||||
});
|
||||
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
name: 'static',
|
||||
id: 'static',
|
||||
let body: any;
|
||||
client.scenario.post(`/v13/deployments`, (req, res) => {
|
||||
body = req.body;
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
});
|
||||
});
|
||||
client.scenario.get(`/v13/deployments/dpl_archive_test`, (req, res) => {
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
readyState: 'READY',
|
||||
aliasAssigned: true,
|
||||
alias: [],
|
||||
});
|
||||
});
|
||||
|
||||
let body: any;
|
||||
client.scenario.post(`/v13/deployments`, (req, res) => {
|
||||
body = req.body;
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
});
|
||||
});
|
||||
client.scenario.get(`/v13/deployments/dpl_archive_test`, (req, res) => {
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
readyState: 'READY',
|
||||
aliasAssigned: true,
|
||||
alias: [],
|
||||
});
|
||||
});
|
||||
|
||||
client.setArgv('deploy', '--prod', '--skip-domain');
|
||||
const exitCode = await deploy(client);
|
||||
expect(exitCode).toEqual(0);
|
||||
expect(body).toMatchObject({
|
||||
target: 'production',
|
||||
source: 'cli',
|
||||
autoAssignCustomDomains: false,
|
||||
version: 2,
|
||||
});
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
client.cwd = setupUnitFixture('commands/deploy/static');
|
||||
client.setArgv('deploy', '--prod', '--skip-domain');
|
||||
const exitCode = await deploy(client);
|
||||
expect(exitCode).toEqual(0);
|
||||
expect(body).toMatchObject({
|
||||
target: 'production',
|
||||
source: 'cli',
|
||||
autoAssignCustomDomains: false,
|
||||
version: 2,
|
||||
});
|
||||
});
|
||||
|
||||
it('should upload missing files', async () => {
|
||||
const cwd = setupUnitFixture('commands/deploy/static');
|
||||
const originalCwd = process.cwd();
|
||||
client.cwd = cwd;
|
||||
|
||||
// Add random 1mb file
|
||||
await fs.writeFile(join(cwd, 'data'), randomBytes(bytes('1mb')));
|
||||
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
name: 'static',
|
||||
id: 'static',
|
||||
});
|
||||
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
name: 'static',
|
||||
id: 'static',
|
||||
});
|
||||
|
||||
let body: any;
|
||||
let fileUploaded = false;
|
||||
client.scenario.post(`/v13/deployments`, (req, res) => {
|
||||
if (fileUploaded) {
|
||||
body = req.body;
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
});
|
||||
} else {
|
||||
const sha = req.body.files[0].sha;
|
||||
res.status(400).json({
|
||||
error: {
|
||||
code: 'missing_files',
|
||||
message: 'Missing files',
|
||||
missing: [sha],
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
client.scenario.post('/v2/files', (req, res) => {
|
||||
// Wait for file to be finished uploading
|
||||
req.on('data', () => {
|
||||
// Noop
|
||||
});
|
||||
req.on('end', () => {
|
||||
fileUploaded = true;
|
||||
res.end();
|
||||
});
|
||||
});
|
||||
client.scenario.get(`/v13/deployments/dpl_archive_test`, (req, res) => {
|
||||
let body: any;
|
||||
let fileUploaded = false;
|
||||
client.scenario.post(`/v13/deployments`, (req, res) => {
|
||||
if (fileUploaded) {
|
||||
body = req.body;
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
readyState: 'READY',
|
||||
aliasAssigned: true,
|
||||
alias: [],
|
||||
});
|
||||
} else {
|
||||
const sha = req.body.files[0].sha;
|
||||
res.status(400).json({
|
||||
error: {
|
||||
code: 'missing_files',
|
||||
message: 'Missing files',
|
||||
missing: [sha],
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
client.scenario.post('/v2/files', (req, res) => {
|
||||
// Wait for file to be finished uploading
|
||||
req.on('data', () => {
|
||||
// Noop
|
||||
});
|
||||
client.scenario.get(
|
||||
`/v10/now/deployments/dpl_archive_test`,
|
||||
(req, res) => {
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
readyState: 'READY',
|
||||
aliasAssigned: true,
|
||||
alias: [],
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// When stderr is not a TTY we expect 5 progress lines to be printed
|
||||
client.stderr.isTTY = false;
|
||||
|
||||
client.setArgv('deploy', '--archive=tgz');
|
||||
const uploadingLines: string[] = [];
|
||||
client.stderr.on('data', data => {
|
||||
if (data.startsWith('Uploading [')) {
|
||||
uploadingLines.push(data);
|
||||
}
|
||||
req.on('end', () => {
|
||||
fileUploaded = true;
|
||||
res.end();
|
||||
});
|
||||
client.stderr.resume();
|
||||
const exitCode = await deploy(client);
|
||||
expect(exitCode).toEqual(0);
|
||||
expect(body?.files?.length).toEqual(1);
|
||||
expect(body?.files?.[0].file).toEqual('.vercel/source.tgz');
|
||||
expect(uploadingLines.length).toEqual(5);
|
||||
expect(
|
||||
uploadingLines[0].startsWith('Uploading [--------------------]')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
uploadingLines[1].startsWith('Uploading [=====---------------]')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
uploadingLines[2].startsWith('Uploading [==========----------]')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
uploadingLines[3].startsWith('Uploading [===============-----]')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
uploadingLines[4].startsWith('Uploading [====================]')
|
||||
).toEqual(true);
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
client.scenario.get(`/v13/deployments/dpl_archive_test`, (req, res) => {
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
readyState: 'READY',
|
||||
aliasAssigned: true,
|
||||
alias: [],
|
||||
});
|
||||
});
|
||||
client.scenario.get(`/v10/now/deployments/dpl_archive_test`, (req, res) => {
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
readyState: 'READY',
|
||||
aliasAssigned: true,
|
||||
alias: [],
|
||||
});
|
||||
});
|
||||
|
||||
// When stderr is not a TTY we expect 5 progress lines to be printed
|
||||
client.stderr.isTTY = false;
|
||||
|
||||
client.setArgv('deploy', '--archive=tgz');
|
||||
const uploadingLines: string[] = [];
|
||||
client.stderr.on('data', data => {
|
||||
if (data.startsWith('Uploading [')) {
|
||||
uploadingLines.push(data);
|
||||
}
|
||||
});
|
||||
client.stderr.resume();
|
||||
const exitCode = await deploy(client);
|
||||
expect(exitCode).toEqual(0);
|
||||
expect(body?.files?.length).toEqual(1);
|
||||
expect(body?.files?.[0].file).toEqual('.vercel/source.tgz');
|
||||
expect(uploadingLines.length).toEqual(5);
|
||||
expect(
|
||||
uploadingLines[0].startsWith('Uploading [--------------------]')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
uploadingLines[1].startsWith('Uploading [=====---------------]')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
uploadingLines[2].startsWith('Uploading [==========----------]')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
uploadingLines[3].startsWith('Uploading [===============-----]')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
uploadingLines[4].startsWith('Uploading [====================]')
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
it('should deploy project linked with `repo.json`', async () => {
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
name: 'app',
|
||||
id: 'QmbKpqpiUqbcke',
|
||||
});
|
||||
|
||||
let body: any;
|
||||
client.scenario.post(`/v13/deployments`, (req, res) => {
|
||||
body = req.body;
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
});
|
||||
});
|
||||
client.scenario.get(`/v13/deployments/dpl_archive_test`, (req, res) => {
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
readyState: 'READY',
|
||||
aliasAssigned: true,
|
||||
alias: [],
|
||||
});
|
||||
});
|
||||
|
||||
const repoRoot = setupUnitFixture('commands/deploy/monorepo-static');
|
||||
client.cwd = join(repoRoot, 'app');
|
||||
client.setArgv('deploy');
|
||||
const exitCode = await deploy(client);
|
||||
expect(exitCode).toEqual(0);
|
||||
expect(body).toMatchObject({
|
||||
source: 'cli',
|
||||
version: 2,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,6 @@ import { useUser } from '../../mocks/user';
|
||||
describe('env', () => {
|
||||
describe('pull', () => {
|
||||
it('should handle pulling', async () => {
|
||||
const cwd = setupUnitFixture('vercel-env-pull');
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
@@ -19,7 +18,9 @@ describe('env', () => {
|
||||
id: 'vercel-env-pull',
|
||||
name: 'vercel-env-pull',
|
||||
});
|
||||
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
|
||||
const cwd = setupUnitFixture('vercel-env-pull');
|
||||
client.cwd = cwd;
|
||||
client.setArgv('env', 'pull', '--yes');
|
||||
const exitCodePromise = env(client);
|
||||
await expect(client.stderr).toOutput(
|
||||
'Downloading `development` Environment Variables for Project vercel-env-pull'
|
||||
@@ -35,7 +36,6 @@ describe('env', () => {
|
||||
});
|
||||
|
||||
it('should handle pulling from Preview env vars', async () => {
|
||||
const cwd = setupUnitFixture('vercel-env-pull');
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
@@ -43,15 +43,9 @@ describe('env', () => {
|
||||
id: 'vercel-env-pull',
|
||||
name: 'vercel-env-pull',
|
||||
});
|
||||
client.setArgv(
|
||||
'env',
|
||||
'pull',
|
||||
'--yes',
|
||||
'--cwd',
|
||||
cwd,
|
||||
'--environment',
|
||||
'preview'
|
||||
);
|
||||
const cwd = setupUnitFixture('vercel-env-pull');
|
||||
client.cwd = cwd;
|
||||
client.setArgv('env', 'pull', '--yes', '--environment', 'preview');
|
||||
const exitCodePromise = env(client);
|
||||
await expect(client.stderr).toOutput(
|
||||
'Downloading `preview` Environment Variables for Project vercel-env-pull'
|
||||
@@ -70,7 +64,6 @@ describe('env', () => {
|
||||
});
|
||||
|
||||
it('should handle pulling from specific Git branch', async () => {
|
||||
const cwd = setupUnitFixture('vercel-env-pull');
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
@@ -78,12 +71,12 @@ describe('env', () => {
|
||||
id: 'vercel-env-pull',
|
||||
name: 'vercel-env-pull',
|
||||
});
|
||||
const cwd = setupUnitFixture('vercel-env-pull');
|
||||
client.cwd = cwd;
|
||||
client.setArgv(
|
||||
'env',
|
||||
'pull',
|
||||
'--yes',
|
||||
'--cwd',
|
||||
cwd,
|
||||
'--environment',
|
||||
'preview',
|
||||
'--git-branch',
|
||||
@@ -114,7 +107,6 @@ describe('env', () => {
|
||||
});
|
||||
|
||||
it('should handle alternate filename', async () => {
|
||||
const cwd = setupUnitFixture('vercel-env-pull');
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
@@ -122,7 +114,9 @@ describe('env', () => {
|
||||
id: 'vercel-env-pull',
|
||||
name: 'vercel-env-pull',
|
||||
});
|
||||
client.setArgv('env', 'pull', 'other.env', '--yes', '--cwd', cwd);
|
||||
const cwd = setupUnitFixture('vercel-env-pull');
|
||||
client.cwd = cwd;
|
||||
client.setArgv('env', 'pull', 'other.env', '--yes');
|
||||
const exitCodePromise = env(client);
|
||||
await expect(client.stderr).toOutput(
|
||||
'Downloading `development` Environment Variables for Project vercel-env-pull'
|
||||
@@ -138,7 +132,6 @@ describe('env', () => {
|
||||
});
|
||||
|
||||
it('should use given environment', async () => {
|
||||
const cwd = setupUnitFixture('vercel-env-pull');
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
@@ -146,15 +139,9 @@ describe('env', () => {
|
||||
id: 'vercel-env-pull',
|
||||
name: 'vercel-env-pull',
|
||||
});
|
||||
|
||||
client.setArgv(
|
||||
'env',
|
||||
'pull',
|
||||
'--environment',
|
||||
'production',
|
||||
'--cwd',
|
||||
cwd
|
||||
);
|
||||
const cwd = setupUnitFixture('vercel-env-pull');
|
||||
client.cwd = cwd;
|
||||
client.setArgv('env', 'pull', '--environment', 'production');
|
||||
const exitCodePromise = env(client);
|
||||
await expect(client.stderr).toOutput(
|
||||
`Downloading \`production\` Environment Variables for Project vercel-env-pull`
|
||||
@@ -172,7 +159,6 @@ describe('env', () => {
|
||||
});
|
||||
|
||||
it('should throw an error when it does not recognize given environment', async () => {
|
||||
const cwd = setupUnitFixture('vercel-env-pull');
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
@@ -180,15 +166,14 @@ describe('env', () => {
|
||||
id: 'vercel-env-pull',
|
||||
name: 'vercel-env-pull',
|
||||
});
|
||||
|
||||
const cwd = setupUnitFixture('vercel-env-pull');
|
||||
client.cwd = cwd;
|
||||
client.setArgv(
|
||||
'env',
|
||||
'pull',
|
||||
'.env.production',
|
||||
'--environment',
|
||||
'something-invalid',
|
||||
'--cwd',
|
||||
cwd
|
||||
'something-invalid'
|
||||
);
|
||||
|
||||
const exitCodePromise = env(client);
|
||||
@@ -200,7 +185,6 @@ describe('env', () => {
|
||||
});
|
||||
|
||||
it('should expose production system env variables', async () => {
|
||||
const cwd = setupUnitFixture('vercel-env-pull');
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
@@ -209,8 +193,9 @@ describe('env', () => {
|
||||
name: 'vercel-env-pull',
|
||||
autoExposeSystemEnvs: true,
|
||||
});
|
||||
|
||||
client.setArgv('env', 'pull', 'other.env', '--yes', '--cwd', cwd);
|
||||
const cwd = setupUnitFixture('vercel-env-pull');
|
||||
client.cwd = cwd;
|
||||
client.setArgv('env', 'pull', 'other.env', '--yes');
|
||||
const exitCodePromise = env(client);
|
||||
await expect(client.stderr).toOutput(
|
||||
'Downloading `development` Environment Variables for Project vercel-env-pull'
|
||||
@@ -228,6 +213,7 @@ describe('env', () => {
|
||||
|
||||
it('should show a delta string', async () => {
|
||||
const cwd = setupUnitFixture('vercel-env-pull-delta');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -237,7 +223,7 @@ describe('env', () => {
|
||||
name: 'env-pull-delta',
|
||||
});
|
||||
|
||||
client.setArgv('env', 'add', 'NEW_VAR', '--cwd', cwd);
|
||||
client.setArgv('env', 'add', 'NEW_VAR');
|
||||
const addPromise = env(client);
|
||||
|
||||
await expect(client.stderr).toOutput('What’s the value of NEW_VAR?');
|
||||
@@ -253,7 +239,7 @@ describe('env', () => {
|
||||
|
||||
await expect(addPromise).resolves.toEqual(0);
|
||||
|
||||
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
|
||||
client.setArgv('env', 'pull', '--yes');
|
||||
const pullPromise = env(client);
|
||||
await expect(client.stderr).toOutput(
|
||||
'Downloading `development` Environment Variables for Project env-pull-delta'
|
||||
@@ -265,13 +251,12 @@ describe('env', () => {
|
||||
|
||||
await expect(pullPromise).resolves.toEqual(0);
|
||||
} finally {
|
||||
client.setArgv('env', 'rm', 'NEW_VAR', '--yes', '--cwd', cwd);
|
||||
client.setArgv('env', 'rm', 'NEW_VAR', '--yes');
|
||||
await env(client);
|
||||
}
|
||||
});
|
||||
|
||||
it('should not show a delta string when it fails to read a file', async () => {
|
||||
const cwd = setupUnitFixture('vercel-env-pull-delta-corrupt');
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
@@ -279,15 +264,15 @@ describe('env', () => {
|
||||
id: 'env-pull-delta-corrupt',
|
||||
name: 'env-pull-delta-corrupt',
|
||||
});
|
||||
|
||||
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
|
||||
const cwd = setupUnitFixture('vercel-env-pull-delta-corrupt');
|
||||
client.cwd = cwd;
|
||||
client.setArgv('env', 'pull', '--yes');
|
||||
const pullPromise = env(client);
|
||||
await expect(client.stderr).toOutput('Updated .env.local file');
|
||||
await expect(pullPromise).resolves.toEqual(0);
|
||||
});
|
||||
|
||||
it('should show that no changes were found', async () => {
|
||||
const cwd = setupUnitFixture('vercel-env-pull-delta-no-changes');
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
@@ -295,8 +280,8 @@ describe('env', () => {
|
||||
id: 'env-pull-delta-no-changes',
|
||||
name: 'env-pull-delta-no-changes',
|
||||
});
|
||||
|
||||
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
|
||||
client.cwd = setupUnitFixture('vercel-env-pull-delta-no-changes');
|
||||
client.setArgv('env', 'pull', '--yes');
|
||||
const pullPromise = env(client);
|
||||
await expect(client.stderr).toOutput('> No changes found.');
|
||||
await expect(client.stderr).toOutput('Updated .env.local file');
|
||||
@@ -305,6 +290,7 @@ describe('env', () => {
|
||||
|
||||
it('should correctly render delta string when env variable has quotes', async () => {
|
||||
const cwd = setupUnitFixture('vercel-env-pull-delta-quotes');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -329,7 +315,7 @@ describe('env', () => {
|
||||
]
|
||||
);
|
||||
|
||||
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
|
||||
client.setArgv('env', 'pull', '--yes');
|
||||
const pullPromise = env(client);
|
||||
await expect(client.stderr).toOutput(
|
||||
'Downloading `development` Environment Variables for Project env-pull-delta'
|
||||
@@ -339,13 +325,14 @@ describe('env', () => {
|
||||
|
||||
await expect(pullPromise).resolves.toEqual(0);
|
||||
} finally {
|
||||
client.setArgv('env', 'rm', 'NEW_VAR', '--yes', '--cwd', cwd);
|
||||
client.setArgv('env', 'rm', 'NEW_VAR', '--yes');
|
||||
await env(client);
|
||||
}
|
||||
});
|
||||
|
||||
it('should correctly render delta string when local env variable has quotes', async () => {
|
||||
const cwd = setupUnitFixture('vercel-env-pull-delta-quotes');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -370,7 +357,7 @@ describe('env', () => {
|
||||
]
|
||||
);
|
||||
|
||||
client.setArgv('env', 'pull', '.env.testquotes', '--yes', '--cwd', cwd);
|
||||
client.setArgv('env', 'pull', '.env.testquotes', '--yes');
|
||||
const pullPromise = env(client);
|
||||
await expect(client.stderr).toOutput(
|
||||
'Downloading `development` Environment Variables for Project env-pull-delta'
|
||||
@@ -380,7 +367,7 @@ describe('env', () => {
|
||||
|
||||
await expect(pullPromise).resolves.toEqual(0);
|
||||
} finally {
|
||||
client.setArgv('env', 'rm', 'NEW_VAR', '--yes', '--cwd', cwd);
|
||||
client.setArgv('env', 'rm', 'NEW_VAR', '--yes');
|
||||
await env(client);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -9,14 +9,13 @@ import type { Project } from '@vercel-internals/types';
|
||||
|
||||
describe('git', () => {
|
||||
describe('connect', () => {
|
||||
const originalCwd = process.cwd();
|
||||
const fixture = (name: string) =>
|
||||
join(__dirname, '../../fixtures/unit/commands/git/connect', name);
|
||||
|
||||
it('connects an unlinked project', async () => {
|
||||
const cwd = fixture('unlinked');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -62,13 +61,12 @@ describe('git', () => {
|
||||
});
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
it('connects an unlinked project with a remote url', async () => {
|
||||
const cwd = fixture('unlinked');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -119,34 +117,28 @@ describe('git', () => {
|
||||
});
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
it('should fail when there is no git config', async () => {
|
||||
const cwd = fixture('no-git-config');
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'no-git-config',
|
||||
name: 'no-git-config',
|
||||
});
|
||||
client.setArgv('git', 'connect', '--yes');
|
||||
const exitCode = await git(client);
|
||||
expect(exitCode).toEqual(1);
|
||||
await expect(client.stderr).toOutput(
|
||||
`Error: No local Git repository found. Run \`git clone <url>\` to clone a remote Git repository first.\n`
|
||||
);
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
client.cwd = fixture('no-git-config');
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'no-git-config',
|
||||
name: 'no-git-config',
|
||||
});
|
||||
client.setArgv('git', 'connect', '--yes');
|
||||
const exitCode = await git(client);
|
||||
expect(exitCode).toEqual(1);
|
||||
await expect(client.stderr).toOutput(
|
||||
`Error: No local Git repository found. Run \`git clone <url>\` to clone a remote Git repository first.\n`
|
||||
);
|
||||
});
|
||||
it('should fail when there is no remote url', async () => {
|
||||
const cwd = fixture('no-remote-url');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -163,13 +155,12 @@ describe('git', () => {
|
||||
);
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
it('should fail when the remote url is bad', async () => {
|
||||
const cwd = fixture('bad-remote-url');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -190,13 +181,12 @@ describe('git', () => {
|
||||
);
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
it('should connect a repo to a project that is not already connected', async () => {
|
||||
const cwd = fixture('new-connection');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -232,13 +222,12 @@ describe('git', () => {
|
||||
});
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
it('should replace an old connection with a new one', async () => {
|
||||
const cwd = fixture('existing-connection');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -285,13 +274,12 @@ describe('git', () => {
|
||||
});
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
it('should exit when an already-connected repo is connected', async () => {
|
||||
const cwd = fixture('new-connection');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -324,13 +312,12 @@ describe('git', () => {
|
||||
expect(exitCode).toEqual(1);
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
it('should fail when it cannot find the repository', async () => {
|
||||
const cwd = fixture('invalid-repo');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -354,13 +341,12 @@ describe('git', () => {
|
||||
expect(exitCode).toEqual(1);
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
it('should connect the default option of multiple remotes', async () => {
|
||||
const cwd = fixture('multiple-remotes');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -404,19 +390,17 @@ describe('git', () => {
|
||||
});
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
});
|
||||
describe('disconnect', () => {
|
||||
const originalCwd = process.cwd();
|
||||
const fixture = (name: string) =>
|
||||
join(__dirname, '../../fixtures/unit/commands/git/connect', name);
|
||||
|
||||
it('should disconnect a repository', async () => {
|
||||
const cwd = fixture('new-connection');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -453,13 +437,12 @@ describe('git', () => {
|
||||
expect(exitCode).toEqual(0);
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
it('should fail if there is no repository to disconnect', async () => {
|
||||
const cwd = fixture('new-connection');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -480,13 +463,12 @@ describe('git', () => {
|
||||
expect(exitCode).toEqual(1);
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
it('should connect a given repository', async () => {
|
||||
const cwd = fixture('no-remote-url');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -522,13 +504,12 @@ describe('git', () => {
|
||||
await expect(gitPromise).resolves.toEqual(0);
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
it('should prompt when it finds a repository', async () => {
|
||||
const cwd = fixture('new-connection');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -571,13 +552,12 @@ describe('git', () => {
|
||||
await expect(gitPromise).resolves.toEqual(0);
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
it('should prompt when it finds multiple remotes', async () => {
|
||||
const cwd = fixture('multiple-remotes');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -621,13 +601,12 @@ describe('git', () => {
|
||||
await expect(gitPromise).resolves.toEqual(0);
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
it('should continue as normal when input matches single git remote', async () => {
|
||||
const cwd = fixture('new-connection');
|
||||
client.cwd = cwd;
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
await fs.rename(join(cwd, 'git'), join(cwd, '.git'));
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
@@ -663,7 +642,6 @@ describe('git', () => {
|
||||
await expect(gitPromise).resolves.toEqual(0);
|
||||
} finally {
|
||||
await fs.rename(join(cwd, '.git'), join(cwd, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { basename, join } from 'path';
|
||||
import { mkdtemp, readJSON, remove } from 'fs-extra';
|
||||
import { readJSON } from 'fs-extra';
|
||||
import link from '../../../src/commands/link';
|
||||
import { client } from '../../mocks/client';
|
||||
import { useUser } from '../../mocks/user';
|
||||
@@ -9,117 +9,100 @@ import {
|
||||
useProject,
|
||||
useUnknownProject,
|
||||
} from '../../mocks/project';
|
||||
import { tmpdir } from 'os';
|
||||
import { setupTmpDir } from '../../helpers/setup-unit-fixture';
|
||||
|
||||
describe('link', () => {
|
||||
const origCwd = process.cwd();
|
||||
|
||||
it('should prompt for link', async () => {
|
||||
const cwd = await mkdtemp(join(tmpdir(), 'cli-'));
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
const { project } = useProject({
|
||||
...defaultProject,
|
||||
id: basename(cwd),
|
||||
name: basename(cwd),
|
||||
});
|
||||
useUnknownProject();
|
||||
const cwd = setupTmpDir();
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
const { project } = useProject({
|
||||
...defaultProject,
|
||||
id: basename(cwd),
|
||||
name: basename(cwd),
|
||||
});
|
||||
useUnknownProject();
|
||||
|
||||
const exitCodePromise = link(client);
|
||||
client.cwd = cwd;
|
||||
const exitCodePromise = link(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Set up');
|
||||
client.stdin.write('y\n');
|
||||
await expect(client.stderr).toOutput('Set up');
|
||||
client.stdin.write('y\n');
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
'Which scope should contain your project?'
|
||||
);
|
||||
client.stdin.write('y\n');
|
||||
await expect(client.stderr).toOutput(
|
||||
'Which scope should contain your project?'
|
||||
);
|
||||
client.stdin.write('y\n');
|
||||
|
||||
await expect(client.stderr).toOutput('Link to it?');
|
||||
client.stdin.write('y\n');
|
||||
await expect(client.stderr).toOutput('Link to it?');
|
||||
client.stdin.write('y\n');
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Linked to ${user.username}/${project.name} (created .vercel and added it to .gitignore)`
|
||||
);
|
||||
await expect(client.stderr).toOutput(
|
||||
`Linked to ${user.username}/${project.name} (created .vercel and added it to .gitignore)`
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
|
||||
const projectJson = await readJSON(join(cwd, '.vercel/project.json'));
|
||||
expect(projectJson.orgId).toEqual(user.id);
|
||||
expect(projectJson.projectId).toEqual(project.id);
|
||||
} finally {
|
||||
process.chdir(origCwd);
|
||||
await remove(cwd);
|
||||
}
|
||||
const projectJson = await readJSON(join(cwd, '.vercel/project.json'));
|
||||
expect(projectJson.orgId).toEqual(user.id);
|
||||
expect(projectJson.projectId).toEqual(project.id);
|
||||
});
|
||||
|
||||
it('should allow specifying `--project` flag', async () => {
|
||||
const cwd = await mkdtemp(join(tmpdir(), 'cli-'));
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
const { project } = useProject({
|
||||
...defaultProject,
|
||||
id: basename(cwd),
|
||||
name: basename(cwd),
|
||||
});
|
||||
useUnknownProject();
|
||||
const cwd = setupTmpDir();
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
const { project } = useProject({
|
||||
...defaultProject,
|
||||
id: basename(cwd),
|
||||
name: basename(cwd),
|
||||
});
|
||||
useUnknownProject();
|
||||
|
||||
client.setArgv('--project', project.name!, '--yes');
|
||||
const exitCodePromise = link(client);
|
||||
client.cwd = cwd;
|
||||
client.setArgv('--project', project.name!, '--yes');
|
||||
const exitCodePromise = link(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Linked to ${user.username}/${project.name} (created .vercel and added it to .gitignore)`
|
||||
);
|
||||
await expect(client.stderr).toOutput(
|
||||
`Linked to ${user.username}/${project.name} (created .vercel and added it to .gitignore)`
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
|
||||
const projectJson = await readJSON(join(cwd, '.vercel/project.json'));
|
||||
expect(projectJson.orgId).toEqual(user.id);
|
||||
expect(projectJson.projectId).toEqual(project.id);
|
||||
} finally {
|
||||
process.chdir(origCwd);
|
||||
await remove(cwd);
|
||||
}
|
||||
const projectJson = await readJSON(join(cwd, '.vercel/project.json'));
|
||||
expect(projectJson.orgId).toEqual(user.id);
|
||||
expect(projectJson.projectId).toEqual(project.id);
|
||||
});
|
||||
|
||||
it('should allow overwriting existing link', async () => {
|
||||
const cwd = await mkdtemp(join(tmpdir(), 'cli-'));
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
const { project: proj1 } = useProject({
|
||||
...defaultProject,
|
||||
id: 'one',
|
||||
name: 'one',
|
||||
});
|
||||
const { project: proj2 } = useProject({
|
||||
...defaultProject,
|
||||
id: 'two',
|
||||
name: 'two',
|
||||
});
|
||||
useUnknownProject();
|
||||
const cwd = setupTmpDir();
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
const { project: proj1 } = useProject({
|
||||
...defaultProject,
|
||||
id: 'one',
|
||||
name: 'one',
|
||||
});
|
||||
const { project: proj2 } = useProject({
|
||||
...defaultProject,
|
||||
id: 'two',
|
||||
name: 'two',
|
||||
});
|
||||
useUnknownProject();
|
||||
|
||||
client.setArgv('--project', proj1.name!, '--yes');
|
||||
await expect(link(client)).resolves.toEqual(0);
|
||||
client.cwd = cwd;
|
||||
client.setArgv('--project', proj1.name!, '--yes');
|
||||
await expect(link(client)).resolves.toEqual(0);
|
||||
|
||||
let projectJson = await readJSON(join(cwd, '.vercel/project.json'));
|
||||
expect(projectJson.orgId).toEqual(user.id);
|
||||
expect(projectJson.projectId).toEqual(proj1.id);
|
||||
let projectJson = await readJSON(join(cwd, '.vercel/project.json'));
|
||||
expect(projectJson.orgId).toEqual(user.id);
|
||||
expect(projectJson.projectId).toEqual(proj1.id);
|
||||
|
||||
client.setArgv('--project', proj2.name!, '--yes');
|
||||
await expect(link(client)).resolves.toEqual(0);
|
||||
client.setArgv('--project', proj2.name!, '--yes');
|
||||
await expect(link(client)).resolves.toEqual(0);
|
||||
|
||||
projectJson = await readJSON(join(cwd, '.vercel/project.json'));
|
||||
expect(projectJson.orgId).toEqual(user.id);
|
||||
expect(projectJson.projectId).toEqual(proj2.id);
|
||||
} finally {
|
||||
process.chdir(origCwd);
|
||||
await remove(cwd);
|
||||
}
|
||||
projectJson = await readJSON(join(cwd, '.vercel/project.json'));
|
||||
expect(projectJson.orgId).toEqual(user.id);
|
||||
expect(projectJson.projectId).toEqual(proj2.id);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,179 +18,160 @@ const fixture = (name: string) =>
|
||||
join(__dirname, '../../fixtures/unit/commands/list', name);
|
||||
|
||||
describe('list', () => {
|
||||
const originalCwd = process.cwd();
|
||||
let teamSlug: string;
|
||||
|
||||
it('should get deployments from a project linked by a directory', async () => {
|
||||
const cwd = fixture('with-team');
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
const user = useUser();
|
||||
const team = useTeams('team_dummy');
|
||||
teamSlug = team[0].slug;
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'with-team',
|
||||
name: 'with-team',
|
||||
});
|
||||
const deployment = useDeployment({ creator: user });
|
||||
|
||||
const user = useUser();
|
||||
const team = useTeams('team_dummy');
|
||||
teamSlug = team[0].slug;
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'with-team',
|
||||
name: 'with-team',
|
||||
});
|
||||
const deployment = useDeployment({ creator: user });
|
||||
client.cwd = fixture('with-team');
|
||||
await list(client);
|
||||
|
||||
await list(client);
|
||||
const lines = createLineIterator(client.stderr);
|
||||
|
||||
const lines = createLineIterator(client.stderr);
|
||||
let line = await lines.next();
|
||||
expect(line.value).toEqual('Retrieving project…');
|
||||
|
||||
let line = await lines.next();
|
||||
expect(line.value).toEqual('Retrieving project…');
|
||||
line = await lines.next();
|
||||
expect(line.value).toEqual(`Fetching deployments in ${team[0].slug}`);
|
||||
|
||||
line = await lines.next();
|
||||
expect(line.value).toEqual(`Fetching deployments in ${team[0].slug}`);
|
||||
line = await lines.next();
|
||||
const { org } = pluckIdentifiersFromDeploymentList(line.value!);
|
||||
expect(org).toEqual(team[0].slug);
|
||||
|
||||
line = await lines.next();
|
||||
const { org } = pluckIdentifiersFromDeploymentList(line.value!);
|
||||
expect(org).toEqual(team[0].slug);
|
||||
// skip next line
|
||||
await lines.next();
|
||||
|
||||
// skip next line
|
||||
await lines.next();
|
||||
line = await lines.next();
|
||||
expect(line.value).toEqual('');
|
||||
|
||||
line = await lines.next();
|
||||
expect(line.value).toEqual('');
|
||||
line = await lines.next();
|
||||
const header = parseSpacedTableRow(line.value!);
|
||||
expect(header).toEqual([
|
||||
'Age',
|
||||
'Deployment',
|
||||
'Status',
|
||||
'Duration',
|
||||
'Username',
|
||||
]);
|
||||
|
||||
line = await lines.next();
|
||||
const header = parseSpacedTableRow(line.value!);
|
||||
expect(header).toEqual([
|
||||
'Age',
|
||||
'Deployment',
|
||||
'Status',
|
||||
'Duration',
|
||||
'Username',
|
||||
]);
|
||||
|
||||
line = await lines.next();
|
||||
const data = parseSpacedTableRow(line.value!);
|
||||
data.shift();
|
||||
expect(data).toEqual([
|
||||
`https://${deployment.url}`,
|
||||
stateString(deployment.state || ''),
|
||||
getDeploymentDuration(deployment),
|
||||
user.username,
|
||||
]);
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
line = await lines.next();
|
||||
const data = parseSpacedTableRow(line.value!);
|
||||
data.shift();
|
||||
expect(data).toEqual([
|
||||
`https://${deployment.url}`,
|
||||
stateString(deployment.state || ''),
|
||||
getDeploymentDuration(deployment),
|
||||
user.username,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should get deployments for linked project where the scope is a user', async () => {
|
||||
const cwd = fixture('with-team');
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'with-team',
|
||||
name: 'with-team',
|
||||
});
|
||||
const deployment = useDeployment({ creator: user });
|
||||
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'with-team',
|
||||
name: 'with-team',
|
||||
});
|
||||
const deployment = useDeployment({ creator: user });
|
||||
client.cwd = fixture('with-team');
|
||||
client.setArgv('-S', user.username);
|
||||
await list(client);
|
||||
|
||||
client.setArgv('-S', user.username);
|
||||
await list(client);
|
||||
const lines = createLineIterator(client.stderr);
|
||||
|
||||
const lines = createLineIterator(client.stderr);
|
||||
let line = await lines.next();
|
||||
expect(line.value).toEqual('Retrieving project…');
|
||||
|
||||
let line = await lines.next();
|
||||
expect(line.value).toEqual('Retrieving project…');
|
||||
line = await lines.next();
|
||||
expect(line.value).toEqual(`Fetching deployments in ${user.username}`);
|
||||
|
||||
line = await lines.next();
|
||||
expect(line.value).toEqual(`Fetching deployments in ${user.username}`);
|
||||
line = await lines.next();
|
||||
const { org } = pluckIdentifiersFromDeploymentList(line.value!);
|
||||
expect(org).toEqual(user.username);
|
||||
|
||||
line = await lines.next();
|
||||
const { org } = pluckIdentifiersFromDeploymentList(line.value!);
|
||||
expect(org).toEqual(user.username);
|
||||
// skip next line
|
||||
await lines.next();
|
||||
|
||||
// skip next line
|
||||
await lines.next();
|
||||
line = await lines.next();
|
||||
expect(line.value).toEqual('');
|
||||
|
||||
line = await lines.next();
|
||||
expect(line.value).toEqual('');
|
||||
line = await lines.next();
|
||||
const header = parseSpacedTableRow(line.value!);
|
||||
expect(header).toEqual(['Age', 'Deployment', 'Status', 'Duration']);
|
||||
|
||||
line = await lines.next();
|
||||
const header = parseSpacedTableRow(line.value!);
|
||||
expect(header).toEqual(['Age', 'Deployment', 'Status', 'Duration']);
|
||||
line = await lines.next();
|
||||
const data = parseSpacedTableRow(line.value!);
|
||||
data.shift();
|
||||
|
||||
line = await lines.next();
|
||||
const data = parseSpacedTableRow(line.value!);
|
||||
data.shift();
|
||||
|
||||
expect(data).toEqual([
|
||||
'https://' + deployment.url,
|
||||
stateString(deployment.state || ''),
|
||||
getDeploymentDuration(deployment),
|
||||
]);
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
expect(data).toEqual([
|
||||
'https://' + deployment.url,
|
||||
stateString(deployment.state || ''),
|
||||
getDeploymentDuration(deployment),
|
||||
]);
|
||||
});
|
||||
|
||||
it('should get the deployments for a specified project', async () => {
|
||||
const cwd = fixture('with-team');
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
const user = useUser();
|
||||
const team = useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'with-team',
|
||||
name: 'with-team',
|
||||
});
|
||||
const deployment = useDeployment({ creator: user });
|
||||
|
||||
const user = useUser();
|
||||
const team = useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'with-team',
|
||||
name: 'with-team',
|
||||
});
|
||||
const deployment = useDeployment({ creator: user });
|
||||
client.cwd = fixture('with-team');
|
||||
client.setArgv(deployment.name);
|
||||
await list(client);
|
||||
|
||||
client.setArgv(deployment.name);
|
||||
await list(client);
|
||||
const lines = createLineIterator(client.stderr);
|
||||
|
||||
const lines = createLineIterator(client.stderr);
|
||||
let line = await lines.next();
|
||||
expect(line.value).toEqual('Retrieving project…');
|
||||
|
||||
let line = await lines.next();
|
||||
expect(line.value).toEqual('Retrieving project…');
|
||||
line = await lines.next();
|
||||
expect(line.value).toEqual(
|
||||
`Fetching deployments in ${teamSlug || team[0].slug}`
|
||||
);
|
||||
|
||||
line = await lines.next();
|
||||
expect(line.value).toEqual(
|
||||
`Fetching deployments in ${teamSlug || team[0].slug}`
|
||||
);
|
||||
line = await lines.next();
|
||||
const { org } = pluckIdentifiersFromDeploymentList(line.value!);
|
||||
expect(org).toEqual(teamSlug || team[0].slug);
|
||||
|
||||
line = await lines.next();
|
||||
const { org } = pluckIdentifiersFromDeploymentList(line.value!);
|
||||
expect(org).toEqual(teamSlug || team[0].slug);
|
||||
// skip next line
|
||||
await lines.next();
|
||||
|
||||
// skip next line
|
||||
await lines.next();
|
||||
line = await lines.next();
|
||||
expect(line.value).toEqual('');
|
||||
|
||||
line = await lines.next();
|
||||
expect(line.value).toEqual('');
|
||||
line = await lines.next();
|
||||
const header = parseSpacedTableRow(line.value!);
|
||||
expect(header).toEqual([
|
||||
'Age',
|
||||
'Deployment',
|
||||
'Status',
|
||||
'Duration',
|
||||
'Username',
|
||||
]);
|
||||
|
||||
line = await lines.next();
|
||||
const header = parseSpacedTableRow(line.value!);
|
||||
expect(header).toEqual([
|
||||
'Age',
|
||||
'Deployment',
|
||||
'Status',
|
||||
'Duration',
|
||||
'Username',
|
||||
]);
|
||||
|
||||
line = await lines.next();
|
||||
const data = parseSpacedTableRow(line.value!);
|
||||
data.shift();
|
||||
expect(data).toEqual([
|
||||
`https://${deployment.url}`,
|
||||
stateString(deployment.state || ''),
|
||||
getDeploymentDuration(deployment),
|
||||
user.username,
|
||||
]);
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
line = await lines.next();
|
||||
const data = parseSpacedTableRow(line.value!);
|
||||
data.shift();
|
||||
expect(data).toEqual([
|
||||
`https://${deployment.url}`,
|
||||
stateString(deployment.state || ''),
|
||||
getDeploymentDuration(deployment),
|
||||
user.username,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
365
packages/cli/test/unit/commands/promote.test.ts
Normal file
365
packages/cli/test/unit/commands/promote.test.ts
Normal file
@@ -0,0 +1,365 @@
|
||||
import chalk from 'chalk';
|
||||
import { client } from '../../mocks/client';
|
||||
import { defaultProject, useProject } from '../../mocks/project';
|
||||
import { Request, Response } from 'express';
|
||||
import promote from '../../../src/commands/promote';
|
||||
import { LastAliasRequest } from '@vercel-internals/types';
|
||||
import { setupUnitFixture } from '../../helpers/setup-unit-fixture';
|
||||
import { useDeployment } from '../../mocks/deployment';
|
||||
import { useTeams } from '../../mocks/team';
|
||||
import { useUser } from '../../mocks/user';
|
||||
import sleep from '../../../src/util/sleep';
|
||||
|
||||
jest.setTimeout(60000);
|
||||
|
||||
describe('promote', () => {
|
||||
it('should error if timeout is invalid', async () => {
|
||||
const { cwd } = initPromoteTest();
|
||||
client.cwd = cwd;
|
||||
client.setArgv('promote', '--yes', '--timeout', 'foo');
|
||||
const exitCodePromise = promote(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Error: Invalid timeout "foo"');
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('should error if invalid deployment ID', async () => {
|
||||
const { cwd } = initPromoteTest();
|
||||
client.cwd = cwd;
|
||||
client.setArgv('promote', '????', '--yes');
|
||||
const exitCodePromise = promote(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
'Error: The provided argument "????" is not a valid deployment ID or URL'
|
||||
);
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('should error if deployment not found', async () => {
|
||||
const { cwd } = initPromoteTest();
|
||||
client.cwd = cwd;
|
||||
client.setArgv('promote', 'foo', '--yes');
|
||||
const exitCodePromise = promote(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Fetching deployment "foo" in ');
|
||||
await expect(client.stderr).toOutput(
|
||||
'Error: Error: Can\'t find the deployment "foo" under the context'
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('should show status when not promoting', async () => {
|
||||
const { cwd } = initPromoteTest();
|
||||
client.cwd = cwd;
|
||||
client.setArgv('promote', '--yes');
|
||||
const exitCodePromise = promote(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
'Checking promotion status of vercel-promote'
|
||||
);
|
||||
await expect(client.stderr).toOutput('No deployment promotion in progress');
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
});
|
||||
|
||||
it('should promote by deployment id', async () => {
|
||||
const { cwd, previousDeployment } = initPromoteTest();
|
||||
client.cwd = cwd;
|
||||
client.setArgv('promote', previousDeployment.id, '--yes');
|
||||
const exitCodePromise = promote(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
await expect(client.stderr).toOutput('Promote in progress');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Success! ${chalk.bold('vercel-promote')} was promoted to ${
|
||||
previousDeployment.url
|
||||
} (${previousDeployment.id})`
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
});
|
||||
|
||||
it('should promote by deployment url', async () => {
|
||||
const { cwd, previousDeployment } = initPromoteTest();
|
||||
client.cwd = cwd;
|
||||
client.setArgv('promote', previousDeployment.url, '--yes');
|
||||
const exitCodePromise = promote(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.url}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
await expect(client.stderr).toOutput('Promote in progress');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Success! ${chalk.bold('vercel-promote')} was promoted to ${
|
||||
previousDeployment.url
|
||||
} (${previousDeployment.id})`
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
});
|
||||
|
||||
it('should get status while promoting', async () => {
|
||||
const { cwd, previousDeployment, project } = initPromoteTest({
|
||||
promotePollCount: 10,
|
||||
});
|
||||
client.cwd = cwd;
|
||||
|
||||
// start the promote
|
||||
client.setArgv('promote', previousDeployment.id, '--yes');
|
||||
promote(client);
|
||||
|
||||
// need to wait for the promote request to be accepted
|
||||
await sleep(300);
|
||||
|
||||
// get the status
|
||||
client.setArgv('promote', '--yes');
|
||||
const exitCodePromise = promote(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Checking promotion status of ${project.name}`
|
||||
);
|
||||
await expect(client.stderr).toOutput(
|
||||
`Success! ${chalk.bold('vercel-promote')} was promoted to ${
|
||||
previousDeployment.url
|
||||
} (${previousDeployment.id})`
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
});
|
||||
|
||||
it('should error if promote request fails', async () => {
|
||||
const { cwd, previousDeployment } = initPromoteTest({
|
||||
promotePollCount: 10,
|
||||
promoteStatusCode: 500,
|
||||
});
|
||||
client.cwd = cwd;
|
||||
client.setArgv('promote', previousDeployment.id, '--yes');
|
||||
const exitCodePromise = promote(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
|
||||
// we need to wait a super long time because fetch will return on 500
|
||||
await expect(client.stderr).toOutput('Response Error (500)', 20000);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('should error if promote fails (no aliases)', async () => {
|
||||
const { cwd, previousDeployment } = initPromoteTest({
|
||||
promoteJobStatus: 'failed',
|
||||
});
|
||||
client.cwd = cwd;
|
||||
client.setArgv('promote', previousDeployment.id, '--yes');
|
||||
const exitCodePromise = promote(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
await expect(client.stderr).toOutput('Promote in progress');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Error: Failed to remap all aliases to the requested deployment ${previousDeployment.url} (${previousDeployment.id})`
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('should error if promote fails (with aliases)', async () => {
|
||||
const { cwd, previousDeployment } = initPromoteTest({
|
||||
promoteAliases: [
|
||||
{
|
||||
alias: { alias: 'foo', deploymentId: 'foo_123' },
|
||||
status: 'completed',
|
||||
},
|
||||
{
|
||||
alias: { alias: 'bar', deploymentId: 'bar_123' },
|
||||
status: 'failed',
|
||||
},
|
||||
],
|
||||
promoteJobStatus: 'failed',
|
||||
});
|
||||
client.cwd = cwd;
|
||||
client.setArgv('promote', previousDeployment.id, '--yes');
|
||||
const exitCodePromise = promote(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
await expect(client.stderr).toOutput('Promote in progress');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Error: Failed to remap all aliases to the requested deployment ${previousDeployment.url} (${previousDeployment.id})`
|
||||
);
|
||||
await expect(client.stderr).toOutput(
|
||||
` ${chalk.green('completed')} foo (foo_123)`
|
||||
);
|
||||
await expect(client.stderr).toOutput(
|
||||
` ${chalk.red('failed')} bar (bar_123)`
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('should error if deployment times out', async () => {
|
||||
const { cwd, previousDeployment } = initPromoteTest({
|
||||
promotePollCount: 10,
|
||||
});
|
||||
client.cwd = cwd;
|
||||
client.setArgv('promote', previousDeployment.id, '--yes', '--timeout', '1');
|
||||
const exitCodePromise = promote(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
await expect(client.stderr).toOutput('Promote in progress');
|
||||
await expect(client.stderr).toOutput(
|
||||
`The promotion exceeded its deadline - rerun ${chalk.bold(
|
||||
`vercel promote ${previousDeployment.id}`
|
||||
)} to try again`,
|
||||
10000
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('should immediately exit after requesting promote', async () => {
|
||||
const { cwd, previousDeployment } = initPromoteTest();
|
||||
client.cwd = cwd;
|
||||
client.setArgv('promote', previousDeployment.id, '--yes', '--timeout', '0');
|
||||
const exitCodePromise = promote(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
await expect(client.stderr).toOutput(
|
||||
`Successfully requested promote of ${chalk.bold('vercel-promote')} to ${
|
||||
previousDeployment.url
|
||||
} (${previousDeployment.id})`
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
});
|
||||
|
||||
it('should error if deployment belongs to different team', async () => {
|
||||
const { cwd, previousDeployment } = initPromoteTest();
|
||||
client.cwd = cwd;
|
||||
previousDeployment.team = {
|
||||
id: 'abc',
|
||||
name: 'abc',
|
||||
slug: 'abc',
|
||||
};
|
||||
client.setArgv('promote', previousDeployment.id, '--yes');
|
||||
const exitCodePromise = promote(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
await expect(client.stderr).toOutput(
|
||||
'Error: Deployment belongs to a different team'
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
type DeploymentAlias = {
|
||||
alias: {
|
||||
alias: string;
|
||||
deploymentId: string;
|
||||
};
|
||||
status: string;
|
||||
};
|
||||
|
||||
function initPromoteTest({
|
||||
promoteAliases = [],
|
||||
promoteJobStatus = 'succeeded',
|
||||
promotePollCount = 2,
|
||||
promoteStatusCode,
|
||||
}: {
|
||||
promoteAliases?: DeploymentAlias[];
|
||||
promoteJobStatus?: LastAliasRequest['jobStatus'];
|
||||
promotePollCount?: number;
|
||||
promoteStatusCode?: number;
|
||||
} = {}) {
|
||||
const cwd = setupUnitFixture('commands/promote/simple-next-site');
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
const { project } = useProject({
|
||||
...defaultProject,
|
||||
id: 'vercel-promote',
|
||||
name: 'vercel-promote',
|
||||
});
|
||||
|
||||
const currentDeployment = useDeployment({ creator: user, project });
|
||||
const previousDeployment = useDeployment({ creator: user, project });
|
||||
|
||||
let pollCounter = 0;
|
||||
let lastAliasRequest: LastAliasRequest | null = null;
|
||||
|
||||
client.scenario.post(
|
||||
'/:version/projects/:project/promote/:id',
|
||||
(req: Request, res: Response) => {
|
||||
if (promoteStatusCode === 500) {
|
||||
res.statusCode = 500;
|
||||
res.end('Server error');
|
||||
return;
|
||||
}
|
||||
|
||||
const { id } = req.params;
|
||||
if (previousDeployment.id !== id) {
|
||||
res.statusCode = 404;
|
||||
res.json({
|
||||
error: { code: 'not_found', message: 'Deployment not found', id },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
lastAliasRequest = {
|
||||
fromDeploymentId: currentDeployment.id,
|
||||
jobStatus: 'in-progress',
|
||||
requestedAt: Date.now(),
|
||||
toDeploymentId: id,
|
||||
type: 'promote',
|
||||
};
|
||||
|
||||
Object.defineProperty(project, 'lastAliasRequest', {
|
||||
get(): LastAliasRequest | null {
|
||||
if (
|
||||
lastAliasRequest &&
|
||||
promotePollCount !== undefined &&
|
||||
pollCounter++ > promotePollCount
|
||||
) {
|
||||
lastAliasRequest.jobStatus = promoteJobStatus;
|
||||
}
|
||||
return lastAliasRequest;
|
||||
},
|
||||
set(value: LastAliasRequest | null) {
|
||||
lastAliasRequest = value;
|
||||
},
|
||||
});
|
||||
|
||||
res.statusCode = 201;
|
||||
res.end();
|
||||
}
|
||||
);
|
||||
|
||||
client.scenario.get(
|
||||
'/:version/projects/:project/promote/aliases',
|
||||
(req, res) => {
|
||||
res.json({
|
||||
aliases: promoteAliases,
|
||||
pagination: null,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
cwd,
|
||||
project,
|
||||
currentDeployment,
|
||||
previousDeployment,
|
||||
};
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { client } from '../../mocks/client';
|
||||
import { defaultProject, useProject } from '../../mocks/project';
|
||||
import { Request, Response } from 'express';
|
||||
import rollback from '../../../src/commands/rollback';
|
||||
import { RollbackJobStatus, RollbackTarget } from '@vercel-internals/types';
|
||||
import type { LastAliasRequest } from '@vercel-internals/types';
|
||||
import { setupUnitFixture } from '../../helpers/setup-unit-fixture';
|
||||
import { useDeployment } from '../../mocks/deployment';
|
||||
import { useTeams } from '../../mocks/team';
|
||||
@@ -13,32 +13,22 @@ import sleep from '../../../src/util/sleep';
|
||||
jest.setTimeout(60000);
|
||||
|
||||
describe('rollback', () => {
|
||||
it('should error if cwd is invalid', async () => {
|
||||
client.setArgv('rollback', '--cwd', __filename);
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
'Error: Support for single file deployments has been removed.'
|
||||
);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('should error if timeout is invalid', async () => {
|
||||
const { cwd } = initRollbackTest();
|
||||
client.setArgv('rollback', '--yes', '--cwd', cwd, '--timeout', 'foo');
|
||||
client.cwd = cwd;
|
||||
client.setArgv('rollback', '--yes', '--timeout', 'foo');
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Error: Invalid timeout "foo"');
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('should error if invalid deployment name', async () => {
|
||||
it('should error if invalid deployment ID', async () => {
|
||||
const { cwd } = initRollbackTest();
|
||||
client.setArgv('rollback', '????', '--yes', '--cwd', cwd);
|
||||
client.cwd = cwd;
|
||||
client.setArgv('rollback', '????', '--yes');
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput(
|
||||
'Error: The provided argument "????" is not a valid deployment ID or URL'
|
||||
);
|
||||
@@ -47,10 +37,10 @@ describe('rollback', () => {
|
||||
|
||||
it('should error if deployment not found', async () => {
|
||||
const { cwd } = initRollbackTest();
|
||||
client.setArgv('rollback', 'foo', '--yes', '--cwd', cwd);
|
||||
client.cwd = cwd;
|
||||
client.setArgv('rollback', 'foo', '--yes');
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput(
|
||||
'Error: Can\'t find the deployment "foo" under the context'
|
||||
);
|
||||
@@ -60,10 +50,10 @@ describe('rollback', () => {
|
||||
|
||||
it('should show status when not rolling back', async () => {
|
||||
const { cwd } = initRollbackTest();
|
||||
client.setArgv('rollback', '--yes', '--cwd', cwd);
|
||||
client.cwd = cwd;
|
||||
client.setArgv('rollback', '--yes');
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput(
|
||||
'Checking rollback status of vercel-rollback'
|
||||
);
|
||||
@@ -74,10 +64,10 @@ describe('rollback', () => {
|
||||
|
||||
it('should rollback by deployment id', async () => {
|
||||
const { cwd, previousDeployment } = initRollbackTest();
|
||||
client.setArgv('rollback', previousDeployment.id, '--yes', '--cwd', cwd);
|
||||
client.cwd = cwd;
|
||||
client.setArgv('rollback', previousDeployment.id, '--yes');
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
@@ -93,10 +83,10 @@ describe('rollback', () => {
|
||||
|
||||
it('should rollback by deployment url', async () => {
|
||||
const { cwd, previousDeployment } = initRollbackTest();
|
||||
client.setArgv('rollback', previousDeployment.url, '--yes', '--cwd', cwd);
|
||||
client.cwd = cwd;
|
||||
client.setArgv('rollback', previousDeployment.url, '--yes');
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.url}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
@@ -114,19 +104,19 @@ describe('rollback', () => {
|
||||
const { cwd, previousDeployment, project } = initRollbackTest({
|
||||
rollbackPollCount: 10,
|
||||
});
|
||||
client.cwd = cwd;
|
||||
|
||||
// start the rollback
|
||||
client.setArgv('rollback', previousDeployment.id, '--yes', '--cwd', cwd);
|
||||
client.setArgv('rollback', previousDeployment.id, '--yes');
|
||||
rollback(client);
|
||||
|
||||
// need to wait for the rollback request to be accepted
|
||||
await sleep(500);
|
||||
await sleep(300);
|
||||
|
||||
// get the status
|
||||
client.setArgv('rollback', '--yes', '--cwd', cwd);
|
||||
client.setArgv('rollback', '--yes');
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Checking rollback status of ${project.name}`
|
||||
);
|
||||
@@ -144,26 +134,28 @@ describe('rollback', () => {
|
||||
rollbackPollCount: 10,
|
||||
rollbackStatusCode: 500,
|
||||
});
|
||||
client.cwd = cwd;
|
||||
|
||||
client.setArgv('rollback', previousDeployment.id, '--yes', '--cwd', cwd);
|
||||
const exitCode = await rollback(client);
|
||||
client.setArgv('rollback', previousDeployment.id, '--yes');
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
expect(exitCode).toBe(1);
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
await expect(client.stderr).toOutput('Response Error (500)');
|
||||
// we need to wait a super long time because fetch will return on 500
|
||||
await expect(client.stderr).toOutput('Response Error (500)', 20000);
|
||||
|
||||
await expect(exitCodePromise).resolves.toEqual(1);
|
||||
});
|
||||
|
||||
it('should error if rollback fails (no aliases)', async () => {
|
||||
const { cwd, previousDeployment } = initRollbackTest({
|
||||
rollbackJobStatus: 'failed',
|
||||
});
|
||||
client.setArgv('rollback', previousDeployment.id, '--yes', '--cwd', cwd);
|
||||
client.cwd = cwd;
|
||||
client.setArgv('rollback', previousDeployment.id, '--yes');
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
@@ -189,10 +181,10 @@ describe('rollback', () => {
|
||||
],
|
||||
rollbackJobStatus: 'failed',
|
||||
});
|
||||
client.setArgv('rollback', previousDeployment.id, '--yes', '--cwd', cwd);
|
||||
client.cwd = cwd;
|
||||
client.setArgv('rollback', previousDeployment.id, '--yes');
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
@@ -214,18 +206,16 @@ describe('rollback', () => {
|
||||
const { cwd, previousDeployment } = initRollbackTest({
|
||||
rollbackPollCount: 10,
|
||||
});
|
||||
client.cwd = cwd;
|
||||
client.setArgv(
|
||||
'rollback',
|
||||
previousDeployment.id,
|
||||
'--yes',
|
||||
'--cwd',
|
||||
cwd,
|
||||
'--timeout',
|
||||
'1s'
|
||||
'1'
|
||||
);
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
@@ -241,18 +231,16 @@ describe('rollback', () => {
|
||||
|
||||
it('should immediately exit after requesting rollback', async () => {
|
||||
const { cwd, previousDeployment } = initRollbackTest();
|
||||
client.cwd = cwd;
|
||||
client.setArgv(
|
||||
'rollback',
|
||||
previousDeployment.id,
|
||||
'--yes',
|
||||
'--cwd',
|
||||
cwd,
|
||||
'--timeout',
|
||||
'0'
|
||||
);
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
@@ -272,10 +260,10 @@ describe('rollback', () => {
|
||||
name: 'abc',
|
||||
slug: 'abc',
|
||||
};
|
||||
client.setArgv('rollback', previousDeployment.id, '--yes', '--cwd', cwd);
|
||||
client.cwd = cwd;
|
||||
client.setArgv('rollback', previousDeployment.id, '--yes');
|
||||
const exitCodePromise = rollback(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Retrieving project…');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Fetching deployment "${previousDeployment.id}" in ${previousDeployment.creator?.username}`
|
||||
);
|
||||
@@ -302,11 +290,11 @@ function initRollbackTest({
|
||||
rollbackStatusCode,
|
||||
}: {
|
||||
rollbackAliases?: RollbackAlias[];
|
||||
rollbackJobStatus?: RollbackJobStatus;
|
||||
rollbackJobStatus?: LastAliasRequest['jobStatus'];
|
||||
rollbackPollCount?: number;
|
||||
rollbackStatusCode?: number;
|
||||
} = {}) {
|
||||
const cwd = setupUnitFixture('vercel-rollback');
|
||||
const cwd = setupUnitFixture('commands/rollback/simple-next-site');
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
const { project } = useProject({
|
||||
@@ -315,9 +303,11 @@ function initRollbackTest({
|
||||
name: 'vercel-rollback',
|
||||
});
|
||||
|
||||
const currentDeployment = useDeployment({ creator: user });
|
||||
const previousDeployment = useDeployment({ creator: user });
|
||||
let lastRollbackTarget: RollbackTarget | null = null;
|
||||
const currentDeployment = useDeployment({ creator: user, project });
|
||||
const previousDeployment = useDeployment({ creator: user, project });
|
||||
|
||||
let pollCounter = 0;
|
||||
let lastAliasRequest: LastAliasRequest | null = null;
|
||||
|
||||
client.scenario.post(
|
||||
'/:version/projects/:project/rollback/:id',
|
||||
@@ -337,30 +327,35 @@ function initRollbackTest({
|
||||
return;
|
||||
}
|
||||
|
||||
lastRollbackTarget = {
|
||||
lastAliasRequest = {
|
||||
fromDeploymentId: currentDeployment.id,
|
||||
jobStatus: 'in-progress',
|
||||
requestedAt: Date.now(),
|
||||
toDeploymentId: id,
|
||||
type: 'rollback',
|
||||
};
|
||||
|
||||
Object.defineProperty(project, 'lastAliasRequest', {
|
||||
get(): LastAliasRequest | null {
|
||||
if (
|
||||
lastAliasRequest &&
|
||||
rollbackPollCount !== undefined &&
|
||||
pollCounter++ > rollbackPollCount
|
||||
) {
|
||||
lastAliasRequest.jobStatus = rollbackJobStatus;
|
||||
}
|
||||
return lastAliasRequest;
|
||||
},
|
||||
set(value: LastAliasRequest | null) {
|
||||
lastAliasRequest = value;
|
||||
},
|
||||
});
|
||||
|
||||
res.statusCode = 201;
|
||||
res.end();
|
||||
}
|
||||
);
|
||||
|
||||
let counter = 0;
|
||||
|
||||
client.scenario.get(`/:version/projects/${project.id}`, (req, res) => {
|
||||
const data = { ...project };
|
||||
if (req.query?.rollbackInfo === 'true') {
|
||||
if (lastRollbackTarget && counter++ > rollbackPollCount) {
|
||||
lastRollbackTarget.jobStatus = rollbackJobStatus;
|
||||
}
|
||||
data.lastRollbackTarget = lastRollbackTarget;
|
||||
}
|
||||
res.json(data);
|
||||
});
|
||||
|
||||
client.scenario.get(
|
||||
'/:version/projects/:project/rollback/aliases',
|
||||
(req, res) => {
|
||||
|
||||
@@ -306,10 +306,9 @@ describe('createGitMeta', () => {
|
||||
}
|
||||
});
|
||||
it('uses the repo url for a connected project', async () => {
|
||||
const originalCwd = process.cwd();
|
||||
const directory = fixture('connected-repo');
|
||||
client.cwd = directory;
|
||||
try {
|
||||
process.chdir(directory);
|
||||
await fs.rename(join(directory, 'git'), join(directory, '.git'));
|
||||
|
||||
useUser();
|
||||
@@ -343,7 +342,6 @@ describe('createGitMeta', () => {
|
||||
});
|
||||
} finally {
|
||||
await fs.rename(join(directory, '.git'), join(directory, 'git'));
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('getLinkedProject', () => {
|
||||
try {
|
||||
link = await getLinkedProject(client, cwd);
|
||||
} catch (err) {
|
||||
error = err;
|
||||
error = err as Error;
|
||||
}
|
||||
|
||||
expect(link).toBeUndefined();
|
||||
@@ -57,7 +57,7 @@ describe('getLinkedProject', () => {
|
||||
try {
|
||||
link = await getLinkedProject(client, cwd);
|
||||
} catch (err) {
|
||||
error = err;
|
||||
error = err as Error;
|
||||
}
|
||||
|
||||
expect(link).toBeUndefined();
|
||||
@@ -69,4 +69,106 @@ describe('getLinkedProject', () => {
|
||||
'Could not retrieve Project Settings. To link your Project, remove the `.vercel` directory and deploy again.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should return link with `project.json`', async () => {
|
||||
const cwd = fixture('vercel-pull-next');
|
||||
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'vercel-pull-next',
|
||||
name: 'vercel-pull-next',
|
||||
});
|
||||
|
||||
const link = await getLinkedProject(client, cwd);
|
||||
if (link.status !== 'linked') {
|
||||
throw new Error('Expected to be linked');
|
||||
}
|
||||
expect(link.org.id).toEqual('team_dummy');
|
||||
expect(link.org.type).toEqual('team');
|
||||
expect(link.project.id).toEqual('vercel-pull-next');
|
||||
expect(link.repoRoot).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return link with `repo.json`', async () => {
|
||||
const cwd = fixture('monorepo-link');
|
||||
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
|
||||
// dashboard
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'QmbKpqpiUqbcke',
|
||||
name: 'monorepo-dashboard',
|
||||
});
|
||||
let link = await getLinkedProject(client, join(cwd, 'dashboard'));
|
||||
if (link.status !== 'linked') {
|
||||
throw new Error('Expected to be linked');
|
||||
}
|
||||
expect(link.org.id).toEqual('team_dummy');
|
||||
expect(link.org.type).toEqual('team');
|
||||
expect(link.project.id).toEqual('QmbKpqpiUqbcke');
|
||||
expect(link.repoRoot).toEqual(cwd);
|
||||
|
||||
// marketing
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'QmX6P93ChNDoZP',
|
||||
name: 'monorepo-marketing',
|
||||
});
|
||||
link = await getLinkedProject(client, join(cwd, 'marketing/subdir'));
|
||||
if (link.status !== 'linked') {
|
||||
throw new Error('Expected to be linked');
|
||||
}
|
||||
expect(link.org.id).toEqual('team_dummy');
|
||||
expect(link.org.type).toEqual('team');
|
||||
expect(link.project.id).toEqual('QmX6P93ChNDoZP');
|
||||
expect(link.repoRoot).toEqual(cwd);
|
||||
|
||||
// blog
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'QmScb7GPQt6gsS',
|
||||
name: 'monorepo-blog',
|
||||
});
|
||||
link = await getLinkedProject(client, join(cwd, 'blog'));
|
||||
if (link.status !== 'linked') {
|
||||
throw new Error('Expected to be linked');
|
||||
}
|
||||
expect(link.org.id).toEqual('team_dummy');
|
||||
expect(link.org.type).toEqual('team');
|
||||
expect(link.project.id).toEqual('QmScb7GPQt6gsS');
|
||||
expect(link.repoRoot).toEqual(cwd);
|
||||
});
|
||||
|
||||
it('should show project selector prompt link with `repo.json`', async () => {
|
||||
const cwd = fixture('monorepo-link');
|
||||
|
||||
useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
id: 'QmbKpqpiUqbcke',
|
||||
name: 'monorepo-dashboard',
|
||||
});
|
||||
|
||||
const linkPromise = getLinkedProject(client, cwd);
|
||||
|
||||
// wait for prompt
|
||||
await expect(client.stderr).toOutput('Please select a Project:');
|
||||
|
||||
// make selection
|
||||
client.stdin.write('\r');
|
||||
|
||||
const link = await linkPromise;
|
||||
if (link.status !== 'linked') {
|
||||
throw new Error('Expected to be linked');
|
||||
}
|
||||
expect(link.org.id).toEqual('team_dummy');
|
||||
expect(link.org.type).toEqual('team');
|
||||
expect(link.project.id).toEqual('QmbKpqpiUqbcke');
|
||||
expect(link.repoRoot).toEqual(cwd);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @vercel/client
|
||||
|
||||
## 12.6.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`c7bcea408`](https://github.com/vercel/vercel/commit/c7bcea408131df2d65338e50ce319a6d8e4a8a82)]:
|
||||
- @vercel/build-utils@6.7.4
|
||||
|
||||
## 12.6.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "12.6.0",
|
||||
"version": "12.6.1",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -35,7 +35,7 @@
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"@vercel/build-utils": "6.7.4",
|
||||
"@vercel/routing-utils": "2.2.1",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"@types/minimatch": "3.0.5",
|
||||
"@types/node": "14.18.33",
|
||||
"@types/semver": "7.3.10",
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"@vercel/build-utils": "6.7.4",
|
||||
"typescript": "4.9.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @vercel/gatsby-plugin-vercel-builder
|
||||
|
||||
## 1.3.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`c7bcea408`](https://github.com/vercel/vercel/commit/c7bcea408131df2d65338e50ce319a6d8e4a8a82)]:
|
||||
- @vercel/build-utils@6.7.4
|
||||
- @vercel/node@2.14.4
|
||||
|
||||
## 1.3.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/gatsby-plugin-vercel-builder",
|
||||
"version": "1.3.5",
|
||||
"version": "1.3.6",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -20,8 +20,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@sinclair/typebox": "0.25.24",
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"@vercel/node": "2.14.3",
|
||||
"@vercel/build-utils": "6.7.4",
|
||||
"@vercel/node": "2.14.4",
|
||||
"@vercel/routing-utils": "2.2.1",
|
||||
"esbuild": "0.14.47",
|
||||
"etag": "1.8.1",
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"@types/node-fetch": "^2.3.0",
|
||||
"@types/tar": "^4.0.0",
|
||||
"@types/yauzl-promise": "2.1.0",
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"@vercel/build-utils": "6.7.4",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"async-retry": "1.3.1",
|
||||
"execa": "^1.0.0",
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.5.1",
|
||||
"@types/node": "14.18.33",
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"@vercel/build-utils": "6.7.4",
|
||||
"@vercel/static-config": "2.0.17",
|
||||
"execa": "3.2.0",
|
||||
"fs-extra": "11.1.0",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @vercel/next
|
||||
|
||||
## 3.8.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [next] Fix `functions` config with App Router ([#9889](https://github.com/vercel/vercel/pull/9889))
|
||||
|
||||
- [next] Pass `pageExtensions` data to `apiLambdaGroups` ([#10015](https://github.com/vercel/vercel/pull/10015))
|
||||
|
||||
- Revert "[next] Update rsc content-type test fixtures" ([#10040](https://github.com/vercel/vercel/pull/10040))
|
||||
|
||||
- Remove usage of `env` from Edge Functions and Middleware ([#10018](https://github.com/vercel/vercel/pull/10018))
|
||||
|
||||
## 3.8.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "3.8.5",
|
||||
"version": "3.8.6",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
@@ -35,7 +35,7 @@
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/text-table": "0.2.1",
|
||||
"@types/webpack-sources": "3.2.0",
|
||||
"@vercel/build-utils": "6.7.3",
|
||||
"@vercel/build-utils": "6.7.4",
|
||||
"@vercel/nft": "0.22.5",
|
||||
"@vercel/routing-utils": "2.2.1",
|
||||
"async-sema": "3.0.1",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user