From 476fbee272383e0db429ffa2273a4767b9cfb09a Mon Sep 17 00:00:00 2001 From: James Calcaben Date: Mon, 8 Oct 2018 05:43:34 -0500 Subject: [PATCH] refactor(buildpack): simplify secure host config --- .../src/WebpackTools/PWADevServer.js | 9 +- .../src/util/__tests__/is-port-open.spec.js | 93 +++++++++++ .../pwa-buildpack/src/util/is-port-open.js | 41 +++++ packages/pwa-devdocs/src/_data/top-nav.yml | 2 +- .../src/_data/venia-pwa-concept.yml | 6 +- packages/pwa-devdocs/src/index.html | 8 +- .../pwa-buildpack/troubleshooting/index.md | 20 ++- .../src/venia-pwa-concept/setup/index.md | 156 +++++++++++------- 8 files changed, 263 insertions(+), 72 deletions(-) create mode 100644 packages/pwa-buildpack/src/util/__tests__/is-port-open.spec.js create mode 100644 packages/pwa-buildpack/src/util/is-port-open.js diff --git a/packages/pwa-buildpack/src/WebpackTools/PWADevServer.js b/packages/pwa-buildpack/src/WebpackTools/PWADevServer.js index 3487fca18d..25f5c74dbe 100644 --- a/packages/pwa-buildpack/src/WebpackTools/PWADevServer.js +++ b/packages/pwa-buildpack/src/WebpackTools/PWADevServer.js @@ -21,7 +21,8 @@ const PWADevServer = { return findPort({ startingPort: 8000, endingPort: 8999, - avoid: reserved + avoid: + brandNew && (await PWADevServer.portsByHostname.values(Number)) }).catch(e => { throw Error( debug.errorMsg( @@ -31,8 +32,12 @@ const PWADevServer = { }); }, async getPersistentDevPort(hostname) { + // const usualPort = await PWADevServer.portsByHostname.get(hostname); + + // const freePort = + const [usualPort, freePort] = await Promise.all([ - PWADevServer.portsByHostname.get(hostname), + , PWADevServer.findFreePort() ]); const port = usualPort === freePort ? usualPort : freePort; diff --git a/packages/pwa-buildpack/src/util/__tests__/is-port-open.spec.js b/packages/pwa-buildpack/src/util/__tests__/is-port-open.spec.js new file mode 100644 index 0000000000..2d369fefb2 --- /dev/null +++ b/packages/pwa-buildpack/src/util/__tests__/is-port-open.spec.js @@ -0,0 +1,93 @@ +jest.mock('net'); +const net = require('net'); +const { EventEmitter } = require('events'); + +const EADDRINUSE = 'EADDRINUSE'; +const EUNKNOWN = 'EUNKNOWN'; +const ELISTENTIMEOUT = 'ELISTENTIMEOUT'; +const ECLOSETIMEOUT = 'ECLOSETIMEOUT'; +class MockServer extends EventEmitter { + constructor({ ipv6, ipv4 } = {}) { + super(); + this.ipv6 = ipv6; + this.ipv4 = ipv4; + this.listen = jest.fn(this.listen); + this.close = jest.fn(this.close); + this.once = jest.fn(this.once); + this.on = jest.fn(this.on); + this.trigger = jest.fn(this.trigger); + } + triggerAsync(...args) { + Promise.resolve().then(() => this.trigger(...args)); + } + listen(_, addr) { + const status = (this.status = + addr === '0.0.0.0' ? this.ipv4 : this.ipv6); + switch (status) { + case EADDRINUSE: + case EUNKNOWN: + const error = new Error(status); + error.code = status; + this.triggerAsync('error', error); + break; + case ELISTENTIMEOUT: + break; + default: + this.triggerAsync('listening'); + break; + } + return this; + } + close() { + if (this.status !== ECLOSETIMEOUT) { + this.triggerAsync('close'); + } + } +} + +const isPortOpen = require('../is-port-open'); + +afterEach(() => jest.resetAllMocks()); +afterAll(() => jest.restoreAllMocks()); + +test('detects bound ipv6 port by creating test server', async () => { + const mockServer = new MockServer({ ipv6: EADDRINUSE }); + net.createServer.mockReturnValue(mockServer); + expect(await isPortOpen(8888)).toBe(false); + expect(mockServer.listen).toHaveBeenCalledWith(8888, expect.anything()); +}); + +test('detects bound ipv4 port by creating test server', async () => { + const mockServer = new MockServer({ ipv4: EADDRINUSE }); + net.createServer.mockReturnValue(mockServer); + expect(await isPortOpen(8889)).toBe(false); + expect(mockServer.listen).toHaveBeenCalledWith(8889, expect.anything()); +}); + +test('handles when both IP families appear to be in use', async () => { + const mockServer = new MockServer({ ipv4: EADDRINUSE, ipv6: EADDRINUSE }); + net.createServer.mockReturnValue(mockServer); + expect(await isPortOpen(8890)).toBe(false); +}); + +test('detects when port is unbound', async () => { + const mockServer = new MockServer(); + net.createServer.mockReturnValue(mockServer); + expect(await isPortOpen(8890)).toBe(true); +}); + +test('handles unexpected errors', async () => { + const mockServer = new MockServer({ ipv4: EUNKNOWN, ipv6: EUNKNOWN }); + net.createServer.mockReturnValue(mockServer); + expect(await isPortOpen(8890)).toBe(false); +}); + +test('handles timeouts and closes open handlers', async () => { + const mockServer = new MockServer({ + ipv4: ELISTENTIMEOUT, + ipv6: ECLOSETIMEOUT + }); + net.createServer.mockReturnValue(mockServer); + expect(await isPortOpen(8890)).toBe(false); + expect(mockServer.close).toHaveBeenCalledTimes(2); +}); diff --git a/packages/pwa-buildpack/src/util/is-port-open.js b/packages/pwa-buildpack/src/util/is-port-open.js new file mode 100644 index 0000000000..90ef4c6a94 --- /dev/null +++ b/packages/pwa-buildpack/src/util/is-port-open.js @@ -0,0 +1,41 @@ +const net = require('net'); + +const addresses = ['0.0.0.0', '::']; // handle ipv4 and ipv6 +async function isPortOpen(port) { + return new Promise(resolve => { + const servers = []; + const timeout = setTimeout(finish(false), 2000); + function finish(isOpen) { + return () => { + clearTimeout(timeout); + servers.forEach(server => { + try { + server.close(); + } catch (e) {} + }); + resolve(isOpen); + }; + } + Promise.all( + addresses.map( + address => + new Promise((innerResolve, innerReject) => { + try { + const server = net.createServer(); + return server + .once('error', innerReject) + .once('listening', () => { + servers.push(server); + innerResolve(server); + }) + .listen(port, address); + } catch (e) { + innerReject(e); + } + }) + ) + ).then(finish(true), finish(false)); + }); +} + +module.exports = isPortOpen; diff --git a/packages/pwa-devdocs/src/_data/top-nav.yml b/packages/pwa-devdocs/src/_data/top-nav.yml index 139f3e3ec6..9b46eeab9f 100644 --- a/packages/pwa-devdocs/src/_data/top-nav.yml +++ b/packages/pwa-devdocs/src/_data/top-nav.yml @@ -7,5 +7,5 @@ - label: UI Components url: /peregrine/ -- label: Venia Theme +- label: Venia Storefront url: /venia-pwa-concept/ diff --git a/packages/pwa-devdocs/src/_data/venia-pwa-concept.yml b/packages/pwa-devdocs/src/_data/venia-pwa-concept.yml index 9e79ccb49f..6b081c472b 100644 --- a/packages/pwa-devdocs/src/_data/venia-pwa-concept.yml +++ b/packages/pwa-devdocs/src/_data/venia-pwa-concept.yml @@ -1,9 +1,9 @@ -title: Venia Theme (Concept) +title: Venia Storefront entries: - label: Overview url: /venia-pwa-concept/ - - label: Venia theme setup + - label: Venia storefront setup url: /venia-pwa-concept/setup/ - label: Simple REST checkout flow @@ -97,4 +97,4 @@ entries: url: /venia-pwa-concept/component/sort/ - label: Title bar - url: /venia-pwa-concept/component/title-bar/ \ No newline at end of file + url: /venia-pwa-concept/component/title-bar/ diff --git a/packages/pwa-devdocs/src/index.html b/packages/pwa-devdocs/src/index.html index 172cc27fa2..87908cdf2b 100644 --- a/packages/pwa-devdocs/src/index.html +++ b/packages/pwa-devdocs/src/index.html @@ -18,9 +18,9 @@

Overview

-

Setup Venia Theme

+

Setup Venia Storefront

- Setup a development environment for the proof-of-concept Magento theme built using PWA Buildpack tools and Peregrine components. + Setup a development environment for the Venia storefront reference theme built using PWA Buildpack tools and Peregrine components.

@@ -56,7 +56,7 @@

Tools and Libraries

Learn UI Components

- Use, extend, or remix Peregrine UI components to create a unique Magento PWA theme + Use, extend, or remix Peregrine UI components to create a unique Magento PWA storefront

@@ -119,7 +119,7 @@

Venia Theme source

- Project source code for a conceptual and customizable PWA theme for Magento 2. + Project source code for a conceptual and customizable PWA storefront for Magento 2. It is built using the Peregrine component library and Buildpack tools.

diff --git a/packages/pwa-devdocs/src/pwa-buildpack/troubleshooting/index.md b/packages/pwa-devdocs/src/pwa-buildpack/troubleshooting/index.md index ab71f705ee..8c062dd2d6 100644 --- a/packages/pwa-devdocs/src/pwa-buildpack/troubleshooting/index.md +++ b/packages/pwa-devdocs/src/pwa-buildpack/troubleshooting/index.md @@ -16,6 +16,8 @@ Paste the result console output into the issue. Thank you! ## Common issues +* [Validation errors when running developer mode](#validation-errors) +* [Venia queries to GraphQL produce validation errors](#graphql-validation-errors) * [Browser displays "Cannot proxy to " error and the console displays `ENOTFOUND`](#cannot-proxy) * [Webpack hangs for a long time before beginning compilation](#webpack-hangs) * [Browser cannot resolve the `.local.pwadev` site](#cannot-resolve-site) @@ -23,6 +25,22 @@ Paste the result console output into the issue. Thank you! ## Resolutions +**Validation errors when running developer mode**{:#validation-errors} + +Make sure you copied over the `example.env` file into a new `.env` file in the `packages/venia-concept` directory. +This file should specify variables for your local development environment. + +**Venia queries to GraphQL produce validation errors**{:#graphql-validation-errors} + +Venia and its GraphQL queries are out of sync with the schema of the connected Magentoi instance. +Make sure your Magento instance is up to date with the latest from Magento 2.3 development branch. + +To test whether your queries are up to date, run the following command in the project root: + +``` sh +npm run validate:venia:gql +``` + **Browser displays "Cannot proxy to " error and the console displays `ENOTFOUND`**{:#cannot-proxy} Make sure your Magento store loads in more than one browser. @@ -71,4 +89,4 @@ You can install higher versions of OpenSSL with [Homebrew] on OSX, [Chocolatey] [create an issue]: https://github.com/magento-research/pwa-buildpack/issues [Slack channel]: https://magentocommeng.slack.com/messages/C71HNKYS2/team/UAFV915FB/ [Homebrew]: https://brew.sh/ -[Chocolatey]: https://chocolatey.org/ \ No newline at end of file +[Chocolatey]: https://chocolatey.org/ diff --git a/packages/pwa-devdocs/src/venia-pwa-concept/setup/index.md b/packages/pwa-devdocs/src/venia-pwa-concept/setup/index.md index 579852046d..ede4f01291 100644 --- a/packages/pwa-devdocs/src/venia-pwa-concept/setup/index.md +++ b/packages/pwa-devdocs/src/venia-pwa-concept/setup/index.md @@ -1,11 +1,11 @@ --- -title: Venia theme setup +title: Venia storefront setup --- -Venia is a PWA theme that runs on top of an existing Magento 2 backend. -Follow the instructions on this page to setup and install the [Venia PWA concept theme][] in your Magento 2 instance. +Venia is a PWA storefront that runs on top of an existing Magento 2 backend. +Follow the instructions on this page to setup and install the [Venia PWA concept storefront][] in your Magento 2 instance. -At the end of this tutorial, you will have a working copy of the Venia theme installed and running on top of Magento. +At the end of this tutorial, you will have a working copy of the Venia storefront installed and running on top of Magento. Use this setup to explore or develop Magento PWA components and themes. If you experience problems with the project setup, see [Troubleshooting][] in the PWA Buildpack section. @@ -21,9 +21,64 @@ If you experience problems with the project setup, see [Troubleshooting][] in th * Magento 2 installed using [valet-plus][] * [Vagrant Box for Magento 2 developers][] +## Step 1. Install Venia sample data +The Venia storefront works best with the new Venia sample data modules installed. -## Step 1. Clone repository +{: .bs-callout .bs-callout-warning} +If you have the previous `magento2-sample-data` module installed, you need to [remove the sample data modules][] and re-install Magento with a clean database. + +In your Magento installation root directory, create a file called `deployVeniaSampleData.sh` with the following content: + +``` sh +#!/usr/bin/env bash +composer='/usr/bin/env composer' +composerParams='--no-interaction --ansi' +moduleVendor='magento' +moduleList=( + module-catalog-sample-data-venia + module-configurable-sample-data-venia + module-customer-sample-data-venia + module-sales-sample-data-venia + module-tax-sample-data-venia + sample-data-media-venia +) +githubBasUrl='git@github.com:PMET-public' + +add_composer_repository () { + name=$1 + type=$2 + url=$3 + echo "adding composer repository ${url}" + ${composer} config ${composerParams} repositories.${name} ${type} ${url} +} + +add_venia_sample_data_repository () { + name=$1 + add_composer_repository ${name} github "${githubBasUrl}/${name}.git" +} + +for moduleName in "${moduleList[@]}" +do + add_venia_sample_data_repository ${moduleName} +done + +${composer} require ${composerParams} $(printf "${moduleVendor}/%s:dev-master@dev " "${moduleList[@]}") +``` + +Execute this script to add the Venia sample data packages to the project: + +``` sh +bash deployVeniaSampleData.sh +``` + +Run the following command to install the Venia data from the modules: + +``` +bin/magento setup:upgrade +``` + +## Step 2. Clone the PWA Studio repository Clone the [PWA Studio] repository into your development environment. @@ -64,14 +119,6 @@ If you clone the PWA Studio project repo into the `magento2ce` directory of the ``` yml ce: "https://github.com/magento/magento2.git::2.3-develop" ``` - And if you want to pull the sample data (optional), update: - ``` yml - ce_sample_data: "git@github.com:magento/magento2-sample-data.git" - ``` - to - ``` yml - ce_sample_data: "https://github.com/magento/magento2-sample-data.git::2.3-develop" - ``` 4. In that same file, update the PHP version to 7.1 by updating the following line: ``` yml php_version: "7.0" @@ -90,7 +137,10 @@ If you clone the PWA Studio project repo into the `magento2ce` directory of the ``` -## Step 2. Install PWA Studio dependencies +## Step 3. Install PWA Studio dependencies + +{: .bs-callout .bs-callout-warning} +If you have an existing `node_modules` directory from a previous PWA Studio version installation, remove it to prevent installation errors. In the PWA Studio project's root directory, run the following command to install the project dependencies: @@ -98,87 +148,71 @@ In the PWA Studio project's root directory, run the following command to install npm install ``` -## Step 3. Link and install module +## Step 4. Set environment variables -Navigate to your Magento installation's `app/code/Magento` directory and create a `Pwa` symlink folder linking to the project's `module` directory. +Under the `packages/venia-concept` directory, copy `.env.dist` into a new `.env` file: -**Example command:** +**Example commands:** ``` sh -ln -s /Users/magedev/pwa-studio/packages/pwa-module Pwa +cd /Users/magedev/pwa-studio/packages/venia-concept ``` -Or from your Magento 2 root ``` sh -ln -s pwa-studio/packages/pwa-module app/code/Magento/Pwa +cp .env.dist .env ``` -### Enable and install - -Navigate to your Magento installation's root director and run the following command to enable the module: +In the `.env` file set the value of `MAGENTO_BACKEND_DOMAIN` to the URL of your Magento development store. -``` sh -bin/magento module:enable Magento_Pwa +**Example:** +``` text +MAGENTO_BACKEND_DOMAIN="https://magento.test/" ``` -Install the module using the following command: -``` sh -bin/magento setup:upgrade -``` +## Step 5. Start the server -## Step 4. Link theme directory +Use any of the following commands from the **project root directory** to start the server: -Navigate to your Magento installation's `app/design/frontend/Magento` directory and create a `venia` symlink folder linking to the project's `theme-frontend-venia` directory. +`npm run watch:venia` -**Example command:** -``` sh -ln -s /Users/magedev/pwa-studio/packages/venia-concept venia -``` +: Starts the Venia storefront development environment. -## Step 5. Activate the Venia theme +`npm run watch:all` -Browse to the Admin section of your Magento store and configure it to use the **Magento Venia** theme. -You can find this configuration using the **Configuration** link in the **Content** tab. +: Runs the full PWA Studio developer experience, which include Venia hot-reloading and concurrent Buildpack/Peregrine rebuilds. -## Step 6. Set environment variables +`npm run build && npm run stage:venia` -Under the Venia project's `theme-frontend-venia` directory, copy `.env.dist` into a new `.env` file and update the variables with the URL to your Magento development store. +: Generates build artifacts and runs the staging environment, which uses more compressed assets and more closely reflects production. -**Example commands:** -``` sh -cd /Users/magedev/pwa-studio/packages/venia-concept -``` -``` sh -cp .env.dist .env -``` - -## Step 7. Start the development server +### Browsing to the application -Use the following command to start the development server: +After the development server is up and running, look for a similar line in the terminal output (the port may differ for your instance): ``` sh -npm start +PWADevServer ready at https://magento-venia.local.pwadev:8001 ``` -{: .bs-callout .bs-callout-info} -**Note:** -Some users have reported using `sudo npm start` to get around permissions. - -After the development server is up and running, look for a similar line in the terminal output (the port will differ for your instance): +OR ``` sh -Project is running at https://magento-venia.local.pwadev:8000/ +Launching staging server... + +https://magento-venia.local.pwadev:51828/ + +Staging server running at the address above. ``` -This is the new address for your PWA frontend. +This is the address for your PWA frontend. You can still use the old address to access the Admin section of Magento, but for PWA development on the frontend, use this new address. -Congratulations! You have set up your development environment for the Venia theme project. +Congratulations! You have set up your development environment for the Venia storefront project. -[Venia PWA concept theme]: https://github.com/magento-research/pwa-studio/tree/master/packages/venia-concept +[Venia PWA concept storefront]: https://github.com/magento-research/pwa-studio/tree/master/packages/venia-concept [Node Package Manager]: https://www.npmjs.com/ [NodeJS 8.x LTS]: https://nodejs.org/en/ [Vagrant Box for Magento 2 developers]: https://github.com/paliarush/magento2-vagrant-for-developers [Troubleshooting]: {{ site.baseurl }}{% link pwa-buildpack/troubleshooting/index.md %} [PWA Studio]: https://github.com/magento-research/pwa-studio [local development instance]: https://devdocs.magento.com/guides/v2.3/install-gde/bk-install-guide.html -[valet-plus]: https://github.com/weprovide/valet-plus \ No newline at end of file +[valet-plus]: https://github.com/weprovide/valet-plus +[remove the sample data modules]: https://devdocs.magento.com/guides/v2.3/install-gde/install/cli/install-cli-sample-data-other.html#inst-sample-remove