diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b314513966..0bd2742409 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,7 +1,7 @@ --- name: Bug Report about: Report a bug you've found in our code! -title: "[bug]: A short, simple sentence describing the bug" +title: '[bug]: A short, simple sentence describing the bug' labels: bug assignees: '' --- @@ -19,44 +19,50 @@ Feel free to remove this section before creating this issue. --> **Describe the bug** + A clear and concise description of what the bug is. **To reproduce** + Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** + A clear and concise description of what you expected to happen. **Screenshots** -If applicable, add screenshots to help explain your problem. -**Additional context** -Add any other context about the problem here. +If applicable, add screenshots to help explain your problem. **Possible solutions** + Add any ideas about possible solutions to the problem here. +**Debug Report** + +Paste the output of `yarn build:report`, and any other context about the problem, here. + **Please complete the following device information:** - - Device [e.g. iPhone6, PC, Mac, Pixel3]: - - OS [e.g. iOS8.1, Windows 10]: - - Browser [e.g. Chrome, Safari]: - - Browser Version [e.g. 22]: - - Magento Version: - - PWA Studio Version: - - NPM version `npm -v`: - - Node Version `node -v`: + +- Device [e.g. iPhone6, PC, Mac, Pixel3]: +- Browser [e.g. Chrome, Safari]: +- Browser Version [e.g. 22]: +- Magento Version [e.g Magento Commerce 2.4]: + **Please let us know what packages this bug is in regards to:** -- [ ] `venia-concept` -- [ ] `venia-ui` -- [ ] `pwa-buildpack` -- [ ] `peregrine` -- [ ] `pwa-devdocs` -- [ ] `upward-js` -- [ ] `upward-spec` -- [ ] `create-pwa` + +- [ ] `venia-concept` +- [ ] `venia-ui` +- [ ] `pwa-buildpack` +- [ ] `peregrine` +- [ ] `pwa-devdocs` +- [ ] `upward-js` +- [ ] `upward-spec` +- [ ] `create-pwa` diff --git a/packages/pwa-buildpack/lib/__fixtures__/mock-package-lock.json b/packages/pwa-buildpack/lib/__fixtures__/mock-package-lock.json new file mode 100644 index 0000000000..a15ca20250 --- /dev/null +++ b/packages/pwa-buildpack/lib/__fixtures__/mock-package-lock.json @@ -0,0 +1,264 @@ +{ + "name": "test1", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@adobe/apollo-link-mutation-queue": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@adobe/apollo-link-mutation-queue/-/apollo-link-mutation-queue-1.0.2.tgz", + "integrity": "sha512-CukTL1XhV0BWuQFK34aSxFBTsavrgw80C0dpVRlhuOXUNmEpFJOHzaVoFjz/1ISRYoYeMWBVMjt9GDopw2oSnA==", + "dev": true, + "requires": { + "apollo-link": "~1.2.13" + } + }, + "@magento/peregrine": { + "version": "file:../../pwa-studio/packages/peregrine/magento-peregrine-8.0.0.tgz", + "integrity": "sha512-ZSuiIjvFYspX4TNIJYGwcEBP5VZxN6+fzUZ664fbpsthDFYXMoRzeSTXRGM7pgv22DZkr/lVUxaw9/zJn9qjeg==", + "dev": true, + "requires": { + "fast-glob": "~3.2.4" + } + }, + "@magento/venia-ui": { + "version": "file:../../pwa-studio/packages/venia-ui/magento-venia-ui-5.0.0.tgz", + "integrity": "sha512-X7FrQrtzNoGxJpbIn/1m/eJ+TdzcFHK1HF+vCbZr/qlXn/yECVaBA0aiwBOskFFHg5y80NrLMj2/djp8/dPjHw==", + "dev": true, + "requires": { + "informed": "~2.11.17", + "jarallax": "~1.11.1", + "load-google-maps-api": "~2.0.1", + "lodash.escape": "~4.0.1", + "lodash.over": "~4.7.0", + "lodash.throttle": "~4.1.1", + "memoize-one": "~5.0.0", + "prop-types": "~15.7.2", + "react-feather": "~2.0.3", + "react-helmet-async": "^1.0.2", + "react-slick": "~0.25.2", + "redux-actions": "~2.6.4", + "redux-thunk": "~2.3.0", + "uuid": "~3.3.2" + } + }, + "@magento/pwa-buildpack": { + "version": "file:../../pwa-studio/packages/pwa-buildpack/magento-pwa-buildpack-7.0.0.tgz", + "integrity": "sha512-MaLX6fLU7zc65qA2oqu8ct2cbBLQTu/WEtFy7bwRrdN9Jz9WaN18CnrJzXlprIwN9qgIgBka7J5WACOhE1vi3w==", + "requires": { + "@magento/directive-parser": "~0.1.7", + "@magento/upward-js": "~5.0.0", + "@pmmmwh/react-refresh-webpack-plugin": "~0.4.1", + "@yarnpkg/lockfile": "~1.1.0", + "apicache": "~1.4.0", + "boxen": "~3.0.0", + "camelspace": "~1.0.0", + "chalk": "~2.4.2", + "copy-webpack-plugin": "~5.0.3", + "debug": "~4.1.1", + "devcert": "~1.1.1", + "dotenv": "~6.2.0", + "enhanced-resolve": "~4.1.1", + "envalid": "~4.2.2", + "errorhandler": "~1.5.1", + "execa": "~1.0.0", + "figures": "~2.0.0", + "fs-extra": "~7.0.1", + "gitignore-to-glob": "~0.3.0", + "graphql-playground-middleware-express": "~1.7.18", + "hastily": "~0.4.9", + "js-yaml": "~3.13.1", + "jsdom": "~16.2.2", + "klaw": "~3.0.0", + "lodash": "~4.17.11", + "memfs": "~3.1.2", + "merge": "~1.2.1", + "micromatch": "~4.0.2", + "node-fetch": "~2.3.0", + "pertain": "~0.2.0", + "pkg-dir": "~4.1.0", + "portscanner": "~2.2.0", + "tapable": "~1.1.3", + "tar": "~4.4.8", + "unionfs": "~4.4.0", + "walk-object": "~4.0.0", + "webpack-assets-manifest": "~3.1.1", + "webpack-inject-plugin": "~1.5.3", + "webpack-node-externals": "~2.5.2", + "webpack-virtual-modules": "~0.3.1", + "word-wrap": "~1.2.3", + "write-file-webpack-plugin": "~4.5.0", + "yargs": "~13.2.2" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "enhanced-resolve": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", + "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "pkg-dir": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.1.0.tgz", + "integrity": "sha512-55k9QN4saZ8q518lE6EFgYiu95u3BWkSajCifhdQjvLvmr8IpnRbhI+UGpWJQfa0KzDguHeeWT1ccO1PmkOi3A==", + "requires": { + "find-up": "^3.0.0" + } + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "yargs": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", + "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.0" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + } + } +} diff --git a/packages/pwa-buildpack/lib/__fixtures__/mock-package.json b/packages/pwa-buildpack/lib/__fixtures__/mock-package.json new file mode 100644 index 0000000000..d14733c508 --- /dev/null +++ b/packages/pwa-buildpack/lib/__fixtures__/mock-package.json @@ -0,0 +1,12 @@ +{ + "name": "test_package", + "version": "0.0.1", + "dependencies": { + "@magento/pwa-buildpack": "7.0.0" + }, + "devDependencies": { + "@adobe/apollo-link-mutation-queue": "1.0.2", + "@magento/peregrine": "8.0.0", + "@magento/venia-ui": "5.0.0" + } +} diff --git a/packages/pwa-buildpack/lib/__fixtures__/mock-yarn.lock b/packages/pwa-buildpack/lib/__fixtures__/mock-yarn.lock new file mode 100644 index 0000000000..5ae45144fd --- /dev/null +++ b/packages/pwa-buildpack/lib/__fixtures__/mock-yarn.lock @@ -0,0 +1,82 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + +"@adobe/apollo-link-mutation-queue@~1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@adobe/apollo-link-mutation-queue/-/apollo-link-mutation-queue-1.0.2.tgz#0c589ffb970b9917ba52c38812740c613c0a40da" + integrity sha512-CukTL1XhV0BWuQFK34aSxFBTsavrgw80C0dpVRlhuOXUNmEpFJOHzaVoFjz/1ISRYoYeMWBVMjt9GDopw2oSnA== + dependencies: + apollo-link "~1.2.13" + +"@magento/peregrine": + version "8.0.0" + resolved "file:///Users/annavara/Documents/node_projects/pwa-studio/packages/peregrine/magento-peregrine-8.0.0.tgz#d2d5d7ebe30b5ab3a6e669745fc510c8f4247198" + dependencies: + fast-glob "~3.2.4" + +"@magento/venia-ui": + version "5.0.0" + uid "1a010839921b5fcec8dfdb39d49027648c38ef39" + resolved "file:../../pwa-studio/packages/venia-ui/magento-venia-ui-5.0.0.tgz#1a010839921b5fcec8dfdb39d49027648c38ef39" + dependencies: + informed "~2.11.17" + jarallax "~1.11.1" + load-google-maps-api "~2.0.1" + lodash.escape "~4.0.1" + lodash.over "~4.7.0" + lodash.throttle "~4.1.1" + memoize-one "~5.0.0" + prop-types "~15.7.2" + react-feather "~2.0.3" + react-helmet-async "^1.0.2" + react-slick "~0.25.2" + redux-actions "~2.6.4" + redux-thunk "~2.3.0" + uuid "~3.3.2" + +"@magento/pwa-buildpack": + version "7.0.0" + resolved "file:../../pwa-studio/packages/pwa-buildpack/magento-pwa-buildpack-7.0.0.tgz#06755053989d8c09219edaba9610ea8280d07cc2" + dependencies: + "@magento/directive-parser" "~0.1.7" + "@magento/upward-js" "~5.0.0" + "@pmmmwh/react-refresh-webpack-plugin" "~0.4.1" + apicache "~1.4.0" + boxen "~3.0.0" + camelspace "~1.0.0" + chalk "~2.4.2" + copy-webpack-plugin "~5.0.3" + debug "~4.1.1" + devcert "~1.1.1" + dotenv "~6.2.0" + enhanced-resolve "~4.1.1" + envalid "~4.2.2" + errorhandler "~1.5.1" + execa "~1.0.0" + figures "~2.0.0" + fs-extra "~7.0.1" + gitignore-to-glob "~0.3.0" + graphql-playground-middleware-express "~1.7.18" + hastily "~0.4.7" + js-yaml "~3.13.1" + jsdom "~16.2.2" + klaw "~3.0.0" + lodash "~4.17.11" + memfs "~3.1.2" + merge "~1.2.1" + micromatch "~4.0.2" + node-fetch "~2.3.0" + pertain "~0.2.0" + pkg-dir "~4.1.0" + portscanner "~2.2.0" + tapable "~1.1.3" + tar "~4.4.8" + unionfs "~4.4.0" + walk-object "~4.0.0" + webpack-assets-manifest "~3.1.1" + webpack-inject-plugin "~1.5.3" + webpack-node-externals "~2.5.2" + webpack-virtual-modules "~0.3.1" + word-wrap "~1.2.3" + write-file-webpack-plugin "~4.5.0" + yargs "~13.2.2" \ No newline at end of file diff --git a/packages/pwa-buildpack/lib/__tests__/__snapshots__/cli-generate-build-report.spec.js.snap b/packages/pwa-buildpack/lib/__tests__/__snapshots__/cli-generate-build-report.spec.js.snap new file mode 100644 index 0000000000..f6c8916441 --- /dev/null +++ b/packages/pwa-buildpack/lib/__tests__/__snapshots__/cli-generate-build-report.spec.js.snap @@ -0,0 +1,122 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should log os version error if wrong version of node is used 1`] = ` +Array [ + "Generating build report for test_package@0.0.1. This may take a moment.", + "Inspecting Dependencies", + "Inspecting Magento Backend", + "Inspecting System", +] +`; + +exports[`should log os version error if wrong version of node is used 2`] = ` +Array [ + " +", + "Found 4 @magento dependencies in yarn.lock", + "@magento/pwa-buildpack @ 7.0.0", + "@adobe/apollo-link-mutation-queue @ 1.0.2", + "@magento/peregrine @ 8.0.0", + "@magento/venia-ui @ 5.0.0", + " +", + "Not using sample backend.", + "Backend is UP!", + " +", + "OS:", + "Unable to fetch OS Version.", + "Node Version:", + "Mock Node Version", + "NPM Version:", + "Mock NPM Version", + " +", +] +`; + +exports[`should log os version error if wrong version of node is used 3`] = `Array []`; + +exports[`should log package info from package-lock.json file if yarn.lock is not available 1`] = ` +Array [ + "Generating build report for test_package@0.0.1. This may take a moment.", + "Inspecting Dependencies", + "Inspecting Magento Backend", + "Inspecting System", +] +`; + +exports[`should log package info from package-lock.json file if yarn.lock is not available 2`] = ` +Array [ + " +", + "Found 4 @magento dependencies in package-lock.json", + "@magento/pwa-buildpack @ file:../../pwa-studio/packages/pwa-buildpack/magento-pwa-buildpack-7.0.0.tgz", + "@adobe/apollo-link-mutation-queue @ 1.0.2", + "@magento/peregrine @ file:../../pwa-studio/packages/peregrine/magento-peregrine-8.0.0.tgz", + "@magento/venia-ui @ file:../../pwa-studio/packages/venia-ui/magento-venia-ui-5.0.0.tgz", + " +", + "Not using sample backend.", + "Backend is UP!", + " +", + "OS:", + "Mock OS Version", + "Node Version:", + "Mock Node Version", + "NPM Version:", + "Mock NPM Version", + " +", +] +`; + +exports[`should log package info from package-lock.json file if yarn.lock is not available 3`] = `Array []`; + +exports[`should log package info from yarn.lock file if available 1`] = ` +Array [ + "Generating build report for test_package@0.0.1. This may take a moment.", + "Inspecting Dependencies", + "Inspecting Magento Backend", + "Inspecting System", +] +`; + +exports[`should log package info from yarn.lock file if available 2`] = ` +Array [ + " +", + "Found 4 @magento dependencies in yarn.lock", + "@magento/pwa-buildpack @ 7.0.0", + "@adobe/apollo-link-mutation-queue @ 1.0.2", + "@magento/peregrine @ 8.0.0", + "@magento/venia-ui @ 5.0.0", + " +", + "Not using sample backend.", + "Backend is UP!", + " +", + "OS:", + "Mock OS Version", + "Node Version:", + "Mock Node Version", + "NPM Version:", + "Mock NPM Version", + " +", +] +`; + +exports[`should log package info from yarn.lock file if available 3`] = `Array []`; + +exports[`should return proper shape 1`] = ` +Object { + "command": "generate-build-report", + "describe": "Generates a report of general build information that can be used when debugging an issue.", + "handler": [Function], +} +`; + +exports[`should throw error if package-lock.json and yarn.lock files are not available 1`] = `[Error: Dependencies have not been installed, please run \`yarn install\` or npm install\` then generate the build report again.]`; diff --git a/packages/pwa-buildpack/lib/__tests__/cli-generate-build-report.spec.js b/packages/pwa-buildpack/lib/__tests__/cli-generate-build-report.spec.js new file mode 100644 index 0000000000..119c63b156 --- /dev/null +++ b/packages/pwa-buildpack/lib/__tests__/cli-generate-build-report.spec.js @@ -0,0 +1,239 @@ +jest.mock('path'); + +const os = require('os'); +const path = require('path'); + +const generateBuildReport = require('../cli/generate-build-report'); +const prettyLogger = require('../util/pretty-logger'); + +const isValidEnv = !!os.version; + +jest.mock('../util/pretty-logger', () => ({ + info: jest.fn().mockName('info'), + log: jest.fn().mockName('log'), + error: jest.fn().mockName('error') +})); + +jest.mock('../cli/create-project', () => ({ + sampleBackends: { + environments: [ + { + name: '2.3.3-venia-cloud', + description: 'Magento 2.3.3 with Venia sample data installed', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/' + } + ] + } +})); + +jest.mock('child_process', () => { + const childProcess = jest.requireActual('child_process'); + + return { + ...childProcess, + spawnSync: jest.fn().mockReturnValue({ + stdout: { + toString: jest.fn().mockReturnValue('Mock NPM Version') + } + }) + }; +}); + +jest.mock('node-fetch', () => + jest.fn().mockImplementation(url => { + if ( + url === + 'https://fvp0esmt8f.execute-api.us-east-1.amazonaws.com/default/getSampleBackends' + ) { + return Promise.resolve({ + json: jest.fn().mockResolvedValue({ + sampleBackends: { + environments: [ + { + name: '2.3.3-venia-cloud', + description: + 'Magento 2.3.3 with Venia sample data installed', + url: + 'https://master-7rqtwti-mfwmkrjfqvbjk.us-4.magentosite.cloud/' + }, + { + name: '2.3.4-venia-cloud', + description: + 'Magento 2.3.4 with Venia sample data installed', + url: 'https://www.magento-backend-2.3.4.com/' + } + ] + } + }) + }); + } else if (url === 'MAGENTO_BACKEND_URL') { + return Promise.resolve({ ok: true }); + } else { + return jest.requireActual('node-fetch')(url); + } + }) +); + +jest.mock('../Utilities', () => ({ + loadEnvironment: jest.fn().mockResolvedValue({ + env: { MAGENTO_BACKEND_URL: 'MAGENTO_BACKEND_URL' } + }) +})); + +const _processVersion = process.version; + +beforeAll(() => { + if (isValidEnv) { + jest.spyOn(os, 'version').mockReturnValue('Mock OS Version'); + } + Object.defineProperty(process, 'version', { + value: 'Mock Node Version' + }); +}); + +afterAll(() => { + Object.defineProperty(process, 'version', { + value: _processVersion + }); +}); + +let logs = []; +let infos = []; +let errors = []; + +beforeEach(() => { + logs = []; + infos = []; + errors = []; + prettyLogger.info = jest.fn().mockImplementation((...args) => { + infos.push(...args); + }); + prettyLogger.log = jest.fn().mockImplementation((...args) => { + logs.push(...args); + }); + prettyLogger.error = jest.fn().mockImplementation((...args) => { + errors.push(...args); + }); + + const cwd = process.cwd(); + const _path = jest.requireActual('path'); + path.resolve.mockImplementation((...pathToResolve) => { + if (pathToResolve.includes('package.json')) { + return _path.resolve( + cwd, + 'packages/pwa-buildpack/lib/__fixtures__/mock-package.json' + ); + } else if (pathToResolve.includes('yarn.lock')) { + return _path.resolve( + cwd, + 'packages/pwa-buildpack/lib/__fixtures__/mock-yarn.lock' + ); + } else if (pathToResolve.includes('package-lock.json')) { + return _path.resolve( + cwd, + 'packages/pwa-buildpack/lib/__fixtures__/mock-package-lock.json' + ); + } else { + return _path.resolve(...pathToResolve); + } + }); +}); + +const testRunner = isValidEnv ? test : test.skip; + +testRunner('should return proper shape', () => { + expect(generateBuildReport).toMatchSnapshot(); +}); + +testRunner( + 'should log package info from yarn.lock file if available', + async () => { + await generateBuildReport.handler(); + + expect(infos).toMatchSnapshot(); + expect(logs).toMatchSnapshot(); + expect(errors).toMatchSnapshot(); + } +); + +testRunner( + 'should log package info from package-lock.json file if yarn.lock is not available', + async () => { + const cwd = process.cwd(); + const _path = jest.requireActual('path'); + path.resolve.mockImplementation((...pathToResolve) => { + if (pathToResolve.includes('package.json')) { + return _path.resolve( + cwd, + 'packages/pwa-buildpack/lib/__fixtures__/mock-package.json' + ); + } else if (pathToResolve.includes('yarn.lock')) { + return _path.resolve( + cwd, + 'packages/pwa-buildpack/lib/__fixtures__/wrong-mock-yarn.lock' + ); + } else if (pathToResolve.includes('package-lock.json')) { + return _path.resolve( + cwd, + 'packages/pwa-buildpack/lib/__fixtures__/mock-package-lock.json' + ); + } else { + return _path.resolve(...pathToResolve); + } + }); + await generateBuildReport.handler(); + + expect(infos).toMatchSnapshot(); + expect(logs).toMatchSnapshot(); + expect(errors).toMatchSnapshot(); + } +); + +testRunner( + 'should throw error if package-lock.json and yarn.lock files are not available', + async () => { + const cwd = process.cwd(); + const _path = jest.requireActual('path'); + path.resolve.mockImplementation((...pathToResolve) => { + if (pathToResolve.includes('package.json')) { + return _path.resolve( + cwd, + 'packages/pwa-buildpack/lib/__fixtures__/mock-package.json' + ); + } else if (pathToResolve.includes('yarn.lock')) { + return _path.resolve( + cwd, + 'packages/pwa-buildpack/lib/__fixtures__/wrong-mock-yarn.lock' + ); + } else if (pathToResolve.includes('package-lock.json')) { + return _path.resolve( + cwd, + 'packages/pwa-buildpack/lib/__fixtures__/wrong-mock-package-lock.json' + ); + } else { + return _path.resolve(...pathToResolve); + } + }); + + try { + await generateBuildReport.handler(); + } catch (err) { + expect(err).toMatchSnapshot(); + } + } +); + +testRunner( + 'should log os version error if wrong version of node is used', + async () => { + os.version.mockImplementationOnce(() => { + throw new Error(); + }); + await generateBuildReport.handler(); + + expect(infos).toMatchSnapshot(); + expect(logs).toMatchSnapshot(); + expect(errors).toMatchSnapshot(); + } +); diff --git a/packages/pwa-buildpack/lib/cli/generate-build-report.js b/packages/pwa-buildpack/lib/cli/generate-build-report.js new file mode 100644 index 0000000000..bfe84b7280 --- /dev/null +++ b/packages/pwa-buildpack/lib/cli/generate-build-report.js @@ -0,0 +1,244 @@ +const { existsSync, readFileSync } = require('fs'); +const { parse } = require('@yarnpkg/lockfile'); +const os = require('os'); +const path = require('path'); +const { spawnSync } = require('child_process'); +const https = require('https'); +const fetch = require('node-fetch'); +const { uniqBy } = require('lodash'); + +const prettyLogger = require('../util/pretty-logger'); +const { loadEnvironment } = require('../Utilities'); +const { sampleBackends: defaultSampleBackends } = require('./create-project'); + +const agent = new https.Agent({ + rejectUnauthorized: false +}); + +async function fetchWithAgent(url) { + return await fetch(url, { agent }); +} + +const removeDuplicateBackends = backendEnvironments => + uniqBy(backendEnvironments, 'url'); + +async function fetchSampleBackendUrls() { + try { + const res = await fetch( + 'https://fvp0esmt8f.execute-api.us-east-1.amazonaws.com/default/getSampleBackends' + ); + const { sampleBackends } = await res.json(); + + return removeDuplicateBackends([ + ...sampleBackends.environments, + ...defaultSampleBackends.environments + ]).map(({ url }) => url); + } catch { + return defaultSampleBackends.environments.map(({ url }) => url); + } +} + +const cwd = process.cwd(); + +function inspectDependencies(pkg) { + prettyLogger.info('Inspecting Dependencies'); + + // Inspect all '@magento/...' '@adobe/...' dependencies. + const dependenciesToScan = [ + ...Object.keys(pkg.dependencies), + ...Object.keys(pkg.devDependencies) + ].filter( + dependency => + dependency.startsWith('@magento/') || + dependency.startsWith('@adobe/') + ); + + let dependencies; + if (existsSync(path.resolve(cwd, 'yarn.lock'))) { + // using yarn + dependencies = getYarnDependencies(dependenciesToScan); + prettyLogger.log( + `Found ${dependencies.size} @magento dependencies in yarn.lock` + ); + } else if (existsSync(path.resolve(cwd, 'package-lock.json'))) { + // using npm + dependencies = getNpmDependencies(dependenciesToScan); + prettyLogger.log( + `Found ${ + dependencies.size + } @magento dependencies in package-lock.json` + ); + } else { + throw new Error( + 'Dependencies have not been installed, please run `yarn install` or npm install` then generate the build report again.' + ); + } + + function logMapElements(values, key) { + values.forEach(value => { + prettyLogger.log(`${key} @ ${value}`); + }); + } + + dependencies.forEach(logMapElements); +} + +function getYarnDependencies(dependencies) { + const dependencyMap = new Map(); + const lockFile = readFileSync(path.resolve(cwd, 'yarn.lock'), 'utf8'); + const parsedLockFile = parse(lockFile); + + dependencies.map(packageName => { + Object.keys(parsedLockFile.object) + .filter(pkgName => pkgName.includes(packageName)) + .forEach(yarnPkgName => { + const version = parsedLockFile.object[yarnPkgName].version; + + if (dependencyMap.has(packageName)) { + if (!dependencyMap.get(packageName).includes(version)) { + dependencyMap.set(yarnPkgName, [ + ...dependencyMap.get(packageName), + version + ]); + } + } else { + dependencyMap.set(packageName, [version]); + } + }); + }); + + return dependencyMap; +} + +function getNpmDependencies(dependencies) { + const dependencyMap = new Map(); + const lockFile = require(path.resolve(cwd, 'package-lock.json')); + + // Inspecting direct dependencies + dependencies.map(packageName => { + const version = lockFile.dependencies[packageName].version; + + if (dependencyMap.has(packageName)) { + if (!dependencyMap.get(packageName).includes(version)) { + dependencyMap.set(packageName, [ + ...dependencyMap.get(packageName), + version + ]); + } + } else { + dependencyMap.set(packageName, [version]); + } + }); + + // Inspecting dependencies of dependencies + Object.values(lockFile.dependencies).map(depConfig => { + if (depConfig.dependencies) { + Object.keys(depConfig.dependencies).map(depConfigDepName => { + if (dependencies.includes(depConfigDepName)) { + const version = + depConfig.dependencies[depConfigDepName].version; + + if (dependencyMap.has(depConfigDepName)) { + if ( + !dependencyMap + .get(depConfigDepName) + .includes(version) + ) { + dependencyMap.set(depConfigDepName, [ + ...dependencyMap.get(depConfigDepName), + version + ]); + } + } else { + dependencyMap.set(depConfigDepName, [version]); + } + } + }); + } + }); + + return dependencyMap; +} + +async function inspectBackend() { + prettyLogger.info('Inspecting Magento Backend'); + const [projectConfig, sampleBackends] = await Promise.all([ + loadEnvironment( + // Load .env from root + path.resolve(cwd) + ), + fetchSampleBackendUrls() + ]); + + if (projectConfig.error) { + throw projectConfig.error; + } + + const { + env: { MAGENTO_BACKEND_URL } + } = projectConfig; + + if (sampleBackends.includes(MAGENTO_BACKEND_URL)) { + prettyLogger.log('Using sample backend: ', MAGENTO_BACKEND_URL); + } else { + prettyLogger.log('Not using sample backend.'); + } + + // Upcheck + try { + await fetchWithAgent(MAGENTO_BACKEND_URL); + prettyLogger.log('Backend is UP!'); + } catch (e) { + prettyLogger.log('Backend is DOWN!'); + prettyLogger.error('Reason:', e); + } +} + +function inspectBuildEnv() { + let osVersion; + try { + osVersion = os.version(); + } catch { + // os.version() is only available from node 13. + osVersion = 'Unable to fetch OS Version.'; + } + const nodeVersion = process.version; + const versionBuffer = spawnSync('npm', ['-v']); + const npmVersion = versionBuffer.stdout.toString(); + + prettyLogger.info('Inspecting System'); + prettyLogger.log('OS:', osVersion); + prettyLogger.log('Node Version:', nodeVersion); + prettyLogger.log('NPM Version:', npmVersion); +} +/** + * main + */ +async function buildpackCli() { + const pkg = require(path.resolve(cwd, 'package.json')); + prettyLogger.info( + `Generating build report for ${pkg.name}@${ + pkg.version + }. This may take a moment.` + ); + prettyLogger.log('\n'); + + inspectDependencies(pkg); + + prettyLogger.log('\n'); + + await inspectBackend(); + + prettyLogger.log('\n'); + + inspectBuildEnv(); + + prettyLogger.log('\n'); +} + +module.exports.command = 'generate-build-report'; + +module.exports.describe = + 'Generates a report of general build information that can be used when debugging an issue.'; + +module.exports.handler = buildpackCli; diff --git a/packages/pwa-buildpack/package.json b/packages/pwa-buildpack/package.json index 1b5cb30c43..13b5a53fb5 100644 --- a/packages/pwa-buildpack/package.json +++ b/packages/pwa-buildpack/package.json @@ -29,6 +29,7 @@ "@magento/directive-parser": "~0.1.7", "@magento/upward-js": "~5.0.0", "@pmmmwh/react-refresh-webpack-plugin": "~0.4.1", + "@yarnpkg/lockfile": "~1.1.0", "apicache": "~1.4.0", "boxen": "~3.0.0", "camelspace": "~1.0.0", diff --git a/packages/venia-concept/_buildpack/create.js b/packages/venia-concept/_buildpack/create.js index 67647dbf8c..dfd0580ebc 100644 --- a/packages/venia-concept/_buildpack/create.js +++ b/packages/venia-concept/_buildpack/create.js @@ -42,6 +42,7 @@ async function createProjectFromVenia({ fs, tasks, options }) { 'build:analyze', 'build:dev', 'build:prod', + 'build:report', 'clean', 'download-schema', 'lint', diff --git a/packages/venia-concept/package.json b/packages/venia-concept/package.json index c68b5f2ef5..cdcb34bd17 100644 --- a/packages/venia-concept/package.json +++ b/packages/venia-concept/package.json @@ -15,6 +15,7 @@ "build:debug": "node --inspect-brk ./node_modules/.bin/webpack --no-progress --env.mode", "build:dev": "yarn run clean && yarn run validate-queries && webpack --no-progress --env.mode development", "build:prod": "yarn run clean && webpack --no-progress --env.mode production", + "build:report": "buildpack generate-build-report", "buildpack": "buildpack", "clean": "rimraf dist", "download-schema": "graphql get-schema --project venia --insecure", diff --git a/yarn.lock b/yarn.lock index c5499edb45..ebf03ca1f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3233,6 +3233,11 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== +"@yarnpkg/lockfile@~1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + abab@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a"