From 4a3815d857ad46d6e406d7ab6e750ca621a47324 Mon Sep 17 00:00:00 2001 From: Manuel <5673677+mtrezza@users.noreply.github.com> Date: Thu, 11 Feb 2021 17:59:34 +0100 Subject: [PATCH] Add tests against multiple Nodejs versions (#7177) * added nodejs version check * added changelog entry * fixed MongoDB ignored versions * fixed nodejs test versions * fixed typo * added changelog entry * added node 15 to CI checks * improved log wording * bumped nodejs default to 14 * added Node 15 to compatibility table --- .github/workflows/ci.yml | 24 +- CHANGELOG.md | 3 +- README.md | 29 +- package-lock.json | 588 ++++++++++++++++++++++++++++++ package.json | 3 +- resources/checkMongodbVersions.js | 168 --------- resources/ci/CiVersionCheck.js | 283 ++++++++++++++ resources/ci/ciCheck.js | 70 ++++ 8 files changed, 982 insertions(+), 186 deletions(-) delete mode 100644 resources/checkMongodbVersions.js create mode 100644 resources/ci/CiVersionCheck.js create mode 100644 resources/ci/ciCheck.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf5fd29334..92cc835d8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,33 +40,43 @@ jobs: MONGODB_VERSION: 4.4.3 MONGODB_TOPOLOGY: replicaset MONGODB_STORAGE_ENGINE: wiredTiger - NODE_VERSION: 10 + NODE_VERSION: 14.15.5 - name: Mongo 4.2, ReplicaSet, WiredTiger MONGODB_VERSION: 4.2.12 MONGODB_TOPOLOGY: replicaset MONGODB_STORAGE_ENGINE: wiredTiger - NODE_VERSION: 10 + NODE_VERSION: 14.15.5 - name: Mongo 4.0, ReplicaSet, WiredTiger MONGODB_VERSION: 4.0.22 MONGODB_TOPOLOGY: replicaset MONGODB_STORAGE_ENGINE: wiredTiger - NODE_VERSION: 10 + NODE_VERSION: 14.15.5 - name: Mongo 3.6, Standalone, MMAPv1 MONGODB_VERSION: 3.6.22 MONGODB_TOPOLOGY: standalone MONGODB_STORAGE_ENGINE: mmapv1 - NODE_VERSION: 10 + NODE_VERSION: 14.15.5 - name: Redis Cache PARSE_SERVER_TEST_CACHE: redis MONGODB_VERSION: 4.4.3 MONGODB_TOPOLOGY: standalone MONGODB_STORAGE_ENGINE: wiredTiger - NODE_VERSION: 10 - - name: Node 12.20 + NODE_VERSION: 14.15.5 + - name: Node 10 MONGODB_VERSION: 4.4.3 MONGODB_TOPOLOGY: standalone MONGODB_STORAGE_ENGINE: wiredTiger - NODE_VERSION: 12.20.1 + NODE_VERSION: 10.23.3 + - name: Node 12 + MONGODB_VERSION: 4.4.3 + MONGODB_TOPOLOGY: standalone + MONGODB_STORAGE_ENGINE: wiredTiger + NODE_VERSION: 12.20.2 + - name: Node 15 + MONGODB_VERSION: 4.4.3 + MONGODB_TOPOLOGY: standalone + MONGODB_STORAGE_ENGINE: wiredTiger + NODE_VERSION: 15.8.0 name: ${{ matrix.name }} timeout-minutes: 30 runs-on: ubuntu-18.04 diff --git a/CHANGELOG.md b/CHANGELOG.md index 2974978f93..5c9e5d7a9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,8 @@ ___ - NEW: LiveQuery support for $and, $nor, $containedBy, $geoWithin, $geoIntersects queries [#7113](https://github.com/parse-community/parse-server/pull/7113). Thanks to [dplewis](https://github.com/dplewis) - NEW: Supporting patterns in LiveQuery server's config parameter `classNames` [#7131](https://github.com/parse-community/parse-server/pull/7131). Thanks to [Nes-si](https://github.com/Nes-si) - IMPROVE: Added new account lockout policy option `accountLockout.unlockOnPasswordReset` to automatically unlock account on password reset. [#7146](https://github.com/parse-community/parse-server/pull/7146). Thanks to [Manuel Trezza](https://github.com/mtrezza). -- IMPROVE: Parse Server will from now on be continuously tested against all relevant MongoDB versions (minor versions). Added MongoDB compatibility table to Parse Server docs. [7161](https://github.com/parse-community/parse-server/pull/7161). Thanks to [Manuel Trezza](https://github.com/mtrezza). +- IMPROVE: Parse Server is from now on continuously tested against all recent MongoDB versions that have not reached their end-of-life support date. Added MongoDB compatibility table to Parse Server docs. [7161](https://github.com/parse-community/parse-server/pull/7161). Thanks to [Manuel Trezza](https://github.com/mtrezza). +- IMPROVE: Parse Server is from now on continuously tested against all recent Node.js versions that have not reached their end-of-life support date. [7161](https://github.com/parse-community/parse-server/pull/7177). Thanks to [Manuel Trezza](https://github.com/mtrezza). - IMPROVE: Optimize queries on classes with pointer permissions. [#7061](https://github.com/parse-community/parse-server/pull/7061). Thanks to [Pedro Diaz](https://github.com/pdiaz) - FIX: request.context for afterFind triggers. [#7078](https://github.com/parse-community/parse-server/pull/7078). Thanks to [dblythy](https://github.com/dblythy) - FIX: Winston Logger interpolating stdout to console [#7114](https://github.com/parse-community/parse-server/pull/7114). Thanks to [dplewis](https://github.com/dplewis) diff --git a/README.md b/README.md index 3310db734e..9eabdf08a4 100644 --- a/README.md +++ b/README.md @@ -103,15 +103,26 @@ Before you start make sure you have installed: ### Compatibility -#### MongoDB Support -Parse Server is continuously tested with the most recent releases of MongoDB to ensure compatibility. The rests run against the latest patch version of each MongoDB release. We follow the [MongoDB support schedule](https://www.mongodb.com/support-policy) and only test against versions that are officially supported by MongoDB and have not reached their end-of-life date yet. +#### Node.js +Parse Server is continuously tested with the most recent releases of Node.js to ensure compatibility. We follow the [Node.js Long Term Support plan](https://github.com/nodejs/Release) and only test against versions that are officially supported and have not reached their end-of-life date yet. + +| Version | Latest Patch Version | End-of-Life Date | Compatibility | +|------------|----------------------|------------------|--------------------| +| Node.js 10 | 10.23.2 | April 2021 | ✅ Fully compatible | +| Node.js 12 | 12.20.1 | April 2022 | ✅ Fully compatible | +| Node.js 14 | 14.15.4 | April 2023 | ✅ Fully compatible | +| Node.js 15 | 15.8.0 | June 2021 | ✅ Fully compatible | + +#### MongoDB +Parse Server is continuously tested with the most recent releases of MongoDB to ensure compatibility. We follow the [MongoDB support schedule](https://www.mongodb.com/support-policy) and only test against versions that are officially supported and have not reached their end-of-life date yet. + +| Version | Latest Patch Version | End-of-Life Date | Compatibility | +|-------------|----------------------|------------------|--------------------| +| MongoDB 3.6 | 3.6.21 | April 2021 | ✅ Fully compatible | +| MongoDB 4.0 | 4.0.22 | January 2022 | ✅ Fully compatible | +| MongoDB 4.2 | 4.2.12 | TBD | ✅ Fully compatible | +| MongoDB 4.4 | 4.4.3 | TBD | ✅ Fully compatible | - | Version | Latest Patch Version | End-of-Life Date | Compatibility | - |-------------|----------------------|------------------|--------------------| - | MongoDB 3.6 | 3.6.21 | April 2021 | ✅ Fully compatible | - | MongoDB 4.0 | 4.0.22 | January 2022 | ✅ Fully compatible | - | MongoDB 4.2 | 4.2.12 | TBD | ✅ Fully compatible | - | MongoDB 4.4 | 4.4.3 | TBD | ✅ Fully compatible | ### Locally ```bash @@ -643,7 +654,7 @@ The following parameter and placeholder keys are reserved because they are used | `pages.enableLocalization` | yes | `Boolean` | `false` | - | `PARSE_SERVER_PAGES_ENABLE_LOCALIZATION` | Is true if pages should be localized; this has no effect on custom page redirects. | | `pages.localizationJsonPath` | yes | `String` | `undefined` | `./private/translations.json` | `PARSE_SERVER_PAGES_LOCALIZATION_JSON_PATH` | The path to the JSON file for localization; the translations will be used to fill template placeholders according to the locale. | | `pages.localizationFallbackLocale` | yes | `String` | `en` | `en`, `en-GB`, `default` | `PARSE_SERVER_PAGES_LOCALIZATION_FALLBACK_LOCALE` | The fallback locale for localization if no matching translation is provided for the given locale. This is only relevant when providing translation resources via JSON file. | -| `pages.placeholders` | yes | `Object`, `Function`, `AsyncFunction` | `undefined` | `{ exampleKey: 'exampleValue' }` | `PARSE_SERVER_PAGES_PLACEHOLDERS` | The placeholder keys and values which will be filled in pages; this can be a simple object or a callback function. | +| `pages.placeholders` | yes | `Object`, `Function`, `AsyncFunction` | `undefined` | `{ exampleKey: 'exampleValue' }` | `PARSE_SERVER_PAGES_PLACEHOLDERS` | The placeholder keys and values which will be filled in pages; this can be a simple object or a callback function. | | `pages.forceRedirect` | yes | `Boolean` | `false` | - | `PARSE_SERVER_PAGES_FORCE_REDIRECT` | Is `true` if responses should always be redirects and never content, `false` if the response type should depend on the request type (`GET` request -> content response; `POST` request -> redirect response). | | `pages.pagesPath` | yes | `String` | `./public` | `./files/pages`, `../../pages` | `PARSE_SERVER_PAGES_PAGES_PATH` | The path to the pages directory; this also defines where the static endpoint `/apps` points to. | | `pages.pagesEndpoint` | yes | `String` | `apps` | - | `PARSE_SERVER_PAGES_PAGES_ENDPOINT` | The API endpoint for the pages. | diff --git a/package-lock.json b/package-lock.json index 72c9c8cac0..01a2bd7794 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1408,6 +1408,69 @@ "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", "dev": true }, + "@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "@napi-rs/triples": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@napi-rs/triples/-/triples-1.0.2.tgz", @@ -1714,6 +1777,21 @@ "any-observable": "^0.3.0" } }, + "@sindresorhus/is": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz", + "integrity": "sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", + "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.0" + } + }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -1736,6 +1814,18 @@ "@types/node": "*" } }, + "@types/cacheable-request": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", + "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, "@types/connect": { "version": "3.4.34", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", @@ -1830,16 +1920,56 @@ "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz", "integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ==" }, + "@types/http-cache-semantics": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", + "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", + "dev": true + }, "@types/http-errors": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz", "integrity": "sha512-2aoSC4UUbHDj2uCsCxcG/vRMXey/m17bC7UwitVm5hn22nI8O8Y9iDpA76Orc+DWkQ4zZrOKEshCqR/jSuXAHA==" }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", + "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, "@types/keygrip": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz", "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==" }, + "@types/keyv": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", + "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/koa": { "version": "2.11.7", "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.11.7.tgz", @@ -1903,6 +2033,15 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/serve-static": { "version": "1.13.9", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", @@ -1925,6 +2064,21 @@ "@types/node": "*" } }, + "@types/yargs": { + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "20.2.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.0.tgz", + "integrity": "sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==", + "dev": true + }, "@types/zen-observable": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.2.tgz", @@ -2035,6 +2189,22 @@ "uri-js": "^4.2.2" } }, + "all-node-versions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/all-node-versions/-/all-node-versions-8.0.0.tgz", + "integrity": "sha512-cF8ibgj23U7ai4qjSFzpeccwDXUlPFMzKe0Z6qf6gChR+9S0JMyzYz6oYz4n0nHi/FLH9BJIefsONsMH/WDM2w==", + "dev": true, + "requires": { + "fetch-node-website": "^5.0.3", + "filter-obj": "^2.0.1", + "get-stream": "^5.1.0", + "global-cache-dir": "^2.0.0", + "jest-validate": "^25.3.0", + "path-exists": "^4.0.0", + "semver": "^7.3.2", + "write-file-atomic": "^3.0.3" + } + }, "ampersand-events": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ampersand-events/-/ampersand-events-2.0.2.tgz", @@ -3128,6 +3298,45 @@ } } }, + "cacheable-lookup": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-2.0.1.tgz", + "integrity": "sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg==", + "dev": true, + "requires": { + "@types/keyv": "^3.1.1", + "keyv": "^4.0.0" + } + }, + "cacheable-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", + "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^2.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, + "cachedir": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", + "dev": true + }, "caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -3346,6 +3555,50 @@ "restore-cursor": "^3.1.0" } }, + "cli-progress": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.9.0.tgz", + "integrity": "sha512-g7rLWfhAo/7pF+a/STFH/xPyosaL1zgADhI0OM83hl3c7S43iGvJWEAV2QuDOnQ8i6EMBj/u4+NTd0d5L+4JfA==", + "dev": true, + "requires": { + "colors": "^1.1.2", + "string-width": "^4.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "cli-truncate": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", @@ -3488,6 +3741,15 @@ "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", "dev": true }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, "clui": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/clui/-/clui-0.3.6.tgz", @@ -4014,6 +4276,12 @@ "clone": "^1.0.2" } }, + "defer-to-connect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", + "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", + "dev": true + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -5133,6 +5401,145 @@ "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" }, + "fetch-node-website": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/fetch-node-website/-/fetch-node-website-5.0.3.tgz", + "integrity": "sha512-O86T46FUWSOq4AWON39oaT8H90QFKAbmjfOVBhgaS87AFfeW00txz73KTv7QopPWtHBbGdI1S8cIT1VK1OQYLg==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "cli-progress": "^3.7.0", + "figures": "^3.2.0", + "filter-obj": "^2.0.1", + "got": "^10.7.0", + "jest-validate": "^25.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "decompress-response": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz", + "integrity": "sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==", + "dev": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, + "got": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/got/-/got-10.7.0.tgz", + "integrity": "sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg==", + "dev": true, + "requires": { + "@sindresorhus/is": "^2.0.0", + "@szmarczak/http-timer": "^4.0.0", + "@types/cacheable-request": "^6.0.1", + "cacheable-lookup": "^2.0.0", + "cacheable-request": "^7.0.1", + "decompress-response": "^5.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^5.0.0", + "lowercase-keys": "^2.0.0", + "mimic-response": "^2.1.0", + "p-cancelable": "^2.0.0", + "p-event": "^4.0.0", + "responselike": "^2.0.0", + "to-readable-stream": "^2.0.0", + "type-fest": "^0.10.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "dev": true + }, + "p-cancelable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", + "dev": true + }, + "p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dev": true, + "requires": { + "p-timeout": "^3.1.0" + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz", + "integrity": "sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw==", + "dev": true + } + } + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -5212,6 +5619,12 @@ } } }, + "filter-obj": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-2.0.1.tgz", + "integrity": "sha512-yDEp513p7+iLdFHWBVdZFnRiOYwg8ZqmpaAiZCMjzqsbo7tCS4Qm4ulXOht337NGzkukKa9u3W4wqQ9tQPm3Ug==", + "dev": true + }, "finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -5641,6 +6054,16 @@ } } }, + "global-cache-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-cache-dir/-/global-cache-dir-2.0.0.tgz", + "integrity": "sha512-30pvU3e8muclEhc9tt+jRMaywOS3QfNdURflJ5Zv0bohjhcVQpBe5bwRHghGSJORLOKW81/n+3iJvHRHs+/S1Q==", + "dev": true, + "requires": { + "cachedir": "^2.3.0", + "path-exists": "^4.0.0" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -5926,6 +6349,12 @@ } } }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, "http-errors": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", @@ -6763,6 +7192,77 @@ "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==", "dev": true }, + "jest-get-type": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", + "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", + "dev": true + }, + "jest-validate": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.5.0.tgz", + "integrity": "sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==", + "dev": true, + "requires": { + "@jest/types": "^25.5.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "jest-get-type": "^25.2.6", + "leven": "^3.1.0", + "pretty-format": "^25.5.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "jmespath": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", @@ -6857,6 +7357,12 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -7040,6 +7546,15 @@ "integrity": "sha1-XqKa/CUppCWThDfWlVtxTOapeR8=", "dev": true }, + "keyv": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", + "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -8401,6 +8916,12 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true + }, "npm-conf": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", @@ -9173,6 +9694,50 @@ "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", "dev": true }, + "pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "dev": true, + "requires": { + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -9609,6 +10174,23 @@ "dev": true, "optional": true }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "dev": true, + "requires": { + "lowercase-keys": "^2.0.0" + }, + "dependencies": { + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -10648,6 +11230,12 @@ } } }, + "to-readable-stream": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-2.1.0.tgz", + "integrity": "sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w==", + "dev": true + }, "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", diff --git a/package.json b/package.json index 7b972d8ca8..10de3c7d9c 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@babel/plugin-transform-flow-strip-types": "7.9.0", "@babel/preset-env": "7.10.0", "@parse/minami": "1.0.0", + "all-node-versions": "8.0.0", "apollo-cache-inmemory": "1.6.6", "apollo-client": "2.6.10", "apollo-link": "1.2.14", @@ -97,7 +98,7 @@ "yaml": "1.10.0" }, "scripts": { - "ci:check": "node ./resources/checkMongodbVersions.js", + "ci:check": "node ./resources/ci/ciCheck.js", "definitions": "node ./resources/buildConfigDefinitions.js && prettier --write 'src/Options/*.js'", "docs": "jsdoc -c ./jsdoc-conf.json", "lint": "flow && eslint --cache ./", diff --git a/resources/checkMongodbVersions.js b/resources/checkMongodbVersions.js deleted file mode 100644 index c835888f61..0000000000 --- a/resources/checkMongodbVersions.js +++ /dev/null @@ -1,168 +0,0 @@ -'use strict' - -const mongoVersionList = require('mongodb-version-list'); -const core = require('@actions/core'); -const semver = require('semver'); -const yaml = require('yaml'); -const fs = require('fs').promises; - -/******************************************************************* - * Settings -********************************************************************/ -// The path to the GitHub workflow YAML file that contains the tests. -const pathToCiYml = './.github/workflows/ci.yml'; -// The key path in the CI YAML file to the environment specifications. -const ciKeyEnvironments = 'jobs.check-mongo.strategy.matrix.include'; -// The key in the CI YAML file to determine the MongoDB version. -const ciKeyVersion = 'MONGODB_VERSION'; -// The versions to ignore when checking whether the CI tests against -// the newest versions. This can be used in case there is a MongoDB -// release for which Parse Server compatibility is not required. -const ignoreReleasedVersions = [ - '4.7.0' // This is a development release according to MongoDB support -]; -/*******************************************************************/ - -/** - * Returns the released versions of MongoDB by MongoDB; this also - * includes pre-releases. - * @return {Array} The released versions. - */ -async function getReleasedVersions () { - return new Promise((resolve, reject) => { - mongoVersionList(function(error, versions) { - if (error) { - reject(error); - } - resolve(versions); - }); - }); -} - -/** - * Returns the test environments in the Github CI as specified in the - * GitHub workflow YAML file. - */ -async function getTests() { - try { - // Get CI workflow - const ciYaml = await fs.readFile(pathToCiYml, 'utf-8'); - const ci = yaml.parse(ciYaml); - - // Extract MongoDB versions - let versions = ciKeyEnvironments.split('.').reduce((o,k) => o !== undefined ? o[k] : undefined, ci); - versions = Object.entries(versions) - .map(entry => entry[1]) - .filter(entry => entry[ciKeyVersion]); - - return versions; - } catch (e) { - throw 'Failed to determine MongoDB versions from CI YAML file with error: ' + e; - } -} - -/** - * Returns all minor and major MongoDB versions against which Parse Server - * is not tested in the CI. - * @param {Array} releasedVersions The released versions. - * @param {Array} testedVersions The tested versions. - * @returns {Array} The untested versions. - */ -function getUntestedMinorsAndMajors(releasedVersions, testedVersions) { - // Get highest tested version - const highestTested = semver.maxSatisfying(testedVersions, '*'); - - // Get all higher released versions (minor & major) - const higherReleased = releasedVersions.reduce((m, v) => { - // If the version is a pre-release, skip it - if ((semver.prerelease(v) || []).length > 0) { - return m; - } - // If the version is not greater than the highest tested version, skip it - if (!semver.gt(v, highestTested)) { - return m; - } - // If the same or a newer version has already been added, skip it - if (semver.maxSatisfying(m, `^${v}`) == v) { - return m; - } - // If there is a higher minor released version, skip it - if (semver.maxSatisfying(releasedVersions, `^${v}`) != v) { - return m; - } - // If version should be ignored, skip it - if (semver.satisfies(v, ignoreReleasedVersions.join(' || '))) { - return m; - } - // Add version - m.push(v); - return m; - }, []); - - return higherReleased; -} - -/** - * Returns the newest patch version for a given version. - * @param {Array} versions The versions in which to search. - * @param {String} version The version for which a newer patch - * version should be searched. - * @returns {String|undefined} The newer patch version. - */ -function getNewerPatch(versions, version) { - const latest = semver.maxSatisfying(versions, `~${version}`); - return semver.gt(latest, version) ? latest : undefined; -} - -/** - * Runs the check. - */ -async function check() { - try { - // Get released MongoDB versions - const releasedVersions = await getReleasedVersions(); - - // Get tested MongoDB versions from CI - const tests = await getTests(); - - // Is true if any of the checks failed - let failed = false; - - // Check whether each tested version is the latest patch - for (const test of tests) { - const version = test[ciKeyVersion]; - const newer = getNewerPatch(releasedVersions, version); - if (newer) { - console.log(`❌ CI environment '${test.name}' uses an old MongoDB patch version ${version} instead of ${newer}.`); - failed = true; - } else { - console.log(`✅ CI environment '${test.name}' uses the newest MongoDB patch version ${version}.`); - } - } - - // Check whether there is a newer minor version available that is not tested - const testedVersions = tests.map(test => test[ciKeyVersion]); - const untested = getUntestedMinorsAndMajors(releasedVersions, testedVersions); - if (untested.length > 0) { - console.log(`❌ CI does not have environments using the following versions of MongoDB: ${untested.join(', ')}.`); - failed = true; - } else { - console.log(`✅ CI environments use all recent major and minor releases of MongoDB.`); - } - - if (failed) { - core.setFailed( - 'CI environments are not up-to-date with newest MongoDB versions.' + - '\n\nCheck the error messages above and update the MongoDB versions in the CI YAML ' + - 'file. There may be versions of MongoDB that have reached their official MongoDB end-of-life ' + - 'support date and should be removed from the CI; see https://www.mongodb.com/support-policy.' - ); - } - - } catch (e) { - core.setFailed('Failed to check MongoDB versions with error: ' + e); - throw 'Failed to check MongoDB versions with error: ' + e; - } -} - -check(); diff --git a/resources/ci/CiVersionCheck.js b/resources/ci/CiVersionCheck.js new file mode 100644 index 0000000000..763d907355 --- /dev/null +++ b/resources/ci/CiVersionCheck.js @@ -0,0 +1,283 @@ +const core = require('@actions/core'); +const semver = require('semver'); +const yaml = require('yaml'); +const fs = require('fs').promises; + +/** + * This checks the CI version of an environment variable in a YAML file + * against a list of released versions of a package. + */ +class CiVersionCheck { + + /** + * The constructor. + * @param {Object} config The config. + * @param {String} config.packageName The package name to check. + * @param {String} config.packageSupportUrl The URL to the package website + * that shows the End-of-Life support dates. + * @param {String} config.yamlFilePath The path to the GitHub workflow YAML + * file that contains the tests. + * @param {String} config.ciEnvironmentsKeyPath The key path in the CI YAML + * file to the environment specifications. + * @param {String} config.ciVersionKey The key in the CI YAML file to + * determine the package version. + * @param {Array} config.releasedVersions The released versions of + * the package to check against. + * @param {Array} config.ignoreReleasedVersions The versions to + * ignore when checking whether the CI tests against the latest versions. + * This can be used in case there is a package release for which Parse + * Server compatibility is not required. + * @param {String} [config.latestComponent='patch'] The version component + * (`major`, `minor`, `patch`) that must be the latest released version. + * Default is `patch`. + * + * For example: + * - Released versions: 1.0.0, 1.2.0, 1.2.1, 1.3.0, 1.3.1, 2.0.0 + * - Tested version: 1.2.0 + * + * If the latest version component is `patch`, then the check would + * fail and recommend an upgrade to version 1.2.1 and to add additional + * tests against 1.3.1 and 2.0.0. + * If the latest version component is `minor` then the check would + * fail and recommend an upgrade to version 1.3.0 and to add an additional + * test against 2.0.0. + * If the latest version component is `major` then the check would + * fail and recommend an upgrade to version 2.0.0. + */ + constructor(config) { + const { + packageName, + packageSupportUrl, + yamlFilePath, + ciEnvironmentsKeyPath, + ciVersionKey, + releasedVersions, + ignoreReleasedVersions = [], + latestComponent = CiVersionCheck.versionComponents.patch, + } = config; + + // Ensure required params are set + if ([ + packageName, + packageSupportUrl, + yamlFilePath, + ciEnvironmentsKeyPath, + ciVersionKey, + releasedVersions, + ].includes(undefined)) { + throw 'invalid configuration'; + } + + if (!Object.keys(CiVersionCheck.versionComponents).includes(latestComponent)) { + throw 'invalid configuration for latestComponent'; + } + + this.packageName = packageName; + this.packageSupportUrl = packageSupportUrl; + this.yamlFilePath = yamlFilePath; + this.ciEnvironmentsKeyPath = ciEnvironmentsKeyPath; + this.ciVersionKey = ciVersionKey; + this.releasedVersions = releasedVersions; + this.ignoreReleasedVersions = ignoreReleasedVersions; + this.latestComponent = latestComponent; + } + + /** + * The definition of version components. + */ + static get versionComponents() { + return Object.freeze({ + major: 'major', + minor: 'minor', + patch: 'patch', + }); + } + + /** + * Returns the test environments as specified in the YAML file. + */ + async getTests() { + try { + // Get CI workflow + const ciYaml = await fs.readFile(this.yamlFilePath, 'utf-8'); + const ci = yaml.parse(ciYaml); + + // Extract package versions + let versions = this.ciEnvironmentsKeyPath.split('.').reduce((o,k) => o !== undefined ? o[k] : undefined, ci); + versions = Object.entries(versions) + .map(entry => entry[1]) + .filter(entry => entry[this.ciVersionKey]); + + return versions; + } catch (e) { + throw `Failed to determine ${this.packageName} versions from CI YAML file with error: ${e}`; + } + } + + /** + * Returns the package versions which are missing in the CI environment. + * @param {Array} releasedVersions The released versions; need to + * be sorted descending. + * @param {Array} testedVersions The tested versions. + * @param {String} versionComponent The latest version component. + * @returns {Array} The untested versions. + */ + getUntestedVersions(releasedVersions, testedVersions, versionComponent) { + // Use these example values for debugging the version range logic below + // versionComponent = CiVersionCheck.versionComponents.patch; + // this.ignoreReleasedVersions = ['<4.4.0', '~4.7.0']; + // testedVersions = ['4.4.3']; + // releasedVersions = [ + // '5.0.0-rc0', + // '5.0.0', + // '4.9.1', + // '4.9.0', + // '4.8.1', + // '4.8.0', + // '4.7.1', + // '4.7.0', + // '4.4.3', + // '4.4.2', + // '4.4.0', + // '4.1.0', + // '3.5.0', + // ]; + + // Determine operator for range comparison + const operator = versionComponent == CiVersionCheck.versionComponents.major + ? '>=' + : versionComponent == CiVersionCheck.versionComponents.minor + ? '^' + : '~' + + // Get all untested versions + const untestedVersions = releasedVersions.reduce((m, v) => { + // If the version should be ignored, skip it + if (this.ignoreReleasedVersions.length > 0 && semver.satisfies(v, this.ignoreReleasedVersions.join(' || '))) { + return m; + } + // If the version is a pre-release, skip it + if ((semver.prerelease(v) || []).length > 0) { + return m; + } + // If a satisfying version has already been added to untested, skip it + if (semver.maxSatisfying(m, `${operator}${v}`)) { + return m; + } + // If a satisfying version is already tested, skip it + if (semver.maxSatisfying(testedVersions, `${operator}${v}`)) { + return m; + } + // Add version + m.push(v); + return m; + }, []); + + return untestedVersions; + } + + /** + * Returns the latest version for a given version and component. + * @param {Array} versions The versions in which to search. + * @param {String} version The version for which a newer version + * should be searched. + * @param {String} versionComponent The version component up to + * which the latest version should be checked. + * @returns {String|undefined} The newer version. + */ + getNewerVersion(versions, version, versionComponent) { + // Determine operator for range comparison + const operator = versionComponent == CiVersionCheck.versionComponents.major + ? '>=' + : versionComponent == CiVersionCheck.versionComponents.minor + ? '^' + : '~' + const latest = semver.maxSatisfying(versions, `${operator}${version}`); + return semver.gt(latest, version) ? latest : undefined; + } + + /** + * This validates that the given versions strictly follow semver + * syntax. + * @param {Array} versions The versions to check. + */ + _validateVersionSyntax(versions) { + for (const version of versions) { + if (!semver.valid(version)) { + throw version; + } + } + } + + /** + * Runs the check. + */ + async check() { + try { + console.log(`\nChecking ${this.packageName} versions in CI environments...`); + + // Validate released versions syntax + try { + this._validateVersionSyntax(this.releasedVersions); + } catch (e) { + core.setFailed(`Failed to check ${this.packageName} versions because released version '${e}' does not follow semver syntax (x.y.z).`); + return; + } + + // Sort versions descending + semver.sort(this.releasedVersions).reverse() + + // Get tested package versions from CI + const tests = await this.getTests(); + + // Is true if any of the checks failed + let failed = false; + + // Check whether each tested version is the latest patch + for (const test of tests) { + const version = test[this.ciVersionKey]; + + // Validate version syntax + try { + this._validateVersionSyntax([version]); + } catch (e) { + core.setFailed(`Failed to check ${this.packageName} versions because environment version '${e}' does not follow semver syntax (x.y.z).`); + return; + } + + const newer = this.getNewerVersion(this.releasedVersions, version, this.latestComponent); + if (newer) { + console.log(`❌ CI environment '${test.name}' uses an old ${this.packageName} ${this.latestComponent} version ${version} instead of ${newer}.`); + failed = true; + } else { + console.log(`✅ CI environment '${test.name}' uses the latest ${this.packageName} ${this.latestComponent} version ${version}.`); + } + } + + // Check whether there is a newer component version available that is not tested + const testedVersions = tests.map(test => test[this.ciVersionKey]); + const untested = this.getUntestedVersions(this.releasedVersions, testedVersions, this.latestComponent); + if (untested.length > 0) { + console.log(`❌ CI does not have environments using the following versions of ${this.packageName}: ${untested.join(', ')}.`); + failed = true; + } else { + console.log(`✅ CI has environments using all recent versions of ${this.packageName}.`); + } + + if (failed) { + core.setFailed( + `CI environments are not up-to-date with the latest ${this.packageName} versions.` + + `\n\nCheck the error messages above and update the ${this.packageName} versions in the CI YAML ` + + `file.\n\nℹ️ Additionally, there may be versions of ${this.packageName} that have reached their official end-of-life ` + + `support date and should be removed from the CI, see ${this.packageSupportUrl}.` + ); + } + + } catch (e) { + const msg = `Failed to check ${this.packageName} versions with error: ${e}`; + core.setFailed(msg); + } + } +} + +module.exports = CiVersionCheck; diff --git a/resources/ci/ciCheck.js b/resources/ci/ciCheck.js new file mode 100644 index 0000000000..66ee6d9aa0 --- /dev/null +++ b/resources/ci/ciCheck.js @@ -0,0 +1,70 @@ +'use strict' + +const CiVersionCheck = require('./CiVersionCheck'); +const mongoVersionList = require('mongodb-version-list'); +const allNodeVersions = require('all-node-versions'); + +async function check() { + // Run checks + await checkMongoDbVersions(); + await checkNodeVersions(); +} + +/** + * Check the MongoDB versions used in test environments. + */ +async function checkMongoDbVersions() { + + const releasedVersions = await new Promise((resolve, reject) => { + mongoVersionList(function(error, versions) { + if (error) { + reject(error); + } + resolve(versions); + }); + }); + + await new CiVersionCheck({ + packageName: 'MongoDB', + packageSupportUrl: 'https://www.mongodb.com/support-policy', + yamlFilePath: './.github/workflows/ci.yml', + ciEnvironmentsKeyPath: 'jobs.check-mongo.strategy.matrix.include', + ciVersionKey: 'MONGODB_VERSION', + releasedVersions, + latestComponent: CiVersionCheck.versionComponents.path, + ignoreReleasedVersions: [ + '<3.6.0', // These versions have reached their MongoDB end-of-life support date + '~3.7.0', // This is a development release according to MongoDB support + '~4.1.0', // This is a development release according to MongoDB support + '~4.3.0', // This is a development release according to MongoDB support + '~4.7.0', // This is a development release according to MongoDB support + ], + }).check(); +} + +/** + * Check the Nodejs versions used in test environments. + */ +async function checkNodeVersions() { + + const allVersions = await allNodeVersions(); + const releasedVersions = allVersions.versions; + + await new CiVersionCheck({ + packageName: 'Node.js', + packageSupportUrl: 'https://github.com/nodejs/node/blob/master/CHANGELOG.md', + yamlFilePath: './.github/workflows/ci.yml', + ciEnvironmentsKeyPath: 'jobs.check-mongo.strategy.matrix.include', + ciVersionKey: 'NODE_VERSION', + releasedVersions, + latestComponent: CiVersionCheck.versionComponents.minor, + ignoreReleasedVersions: [ + '<10.0.0', // These versions have reached their end-of-life support date + '>=11.0.0 <12.0.0', // These versions have reached their end-of-life support date + '>=13.0.0 <14.0.0', // These versions have reached their end-of-life support date + '>=16.0.0', // This version has not been officially released yet + ], + }).check(); +} + +check();