From 9e480b578d37efd563ef91d378c0874e4371c630 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 10 Aug 2023 16:05:20 -0700 Subject: [PATCH 1/6] Cache WordPress version and use it when WordPress.org is unavailable --- packages/env/lib/config/parse-config.js | 2 +- packages/env/lib/wordpress.js | 38 ++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/packages/env/lib/config/parse-config.js b/packages/env/lib/config/parse-config.js index 743db97896344..3f69bb78b81f2 100644 --- a/packages/env/lib/config/parse-config.js +++ b/packages/env/lib/config/parse-config.js @@ -534,7 +534,7 @@ async function parseEnvironmentConfig( async function parseCoreSource( coreSource, options ) { // An empty source means we should use the latest version of WordPress. if ( ! coreSource ) { - const wpVersion = await getLatestWordPressVersion(); + const wpVersion = await getLatestWordPressVersion( options ); if ( ! wpVersion ) { throw new ValidationError( 'Could not find the latest WordPress version. There may be a network issue.' diff --git a/packages/env/lib/wordpress.js b/packages/env/lib/wordpress.js index f238288498cf5..2b56faa48d71e 100644 --- a/packages/env/lib/wordpress.js +++ b/packages/env/lib/wordpress.js @@ -7,12 +7,18 @@ const util = require( 'util' ); const fs = require( 'fs' ).promises; const path = require( 'path' ); const got = require( 'got' ); +const dns = require( 'dns' ).promises; /** * Promisified dependencies */ const copyDir = util.promisify( require( 'copy-dir' ) ); +/** + * Internal dependencies + */ +const { getCache, setCache } = require( './cache' ); + /** * @typedef {import('./config').WPConfig} WPConfig * @typedef {import('./config').WPEnvironmentConfig} WPEnvironmentConfig @@ -243,19 +249,48 @@ async function readWordPressVersion( coreSource, spinner, debug ) { return versionMatch[ 1 ]; } +/** + * Basically a quick check to see if we can connect to the internet. + * + * @return {boolean} True if we can connect to WordPress.org, false otherwise. + */ +async function canAccessWPORG() { + return !! ( await dns.resolve( 'WordPress.org' ).catch( () => {} ) ); +} + /** * Returns the latest stable version of WordPress by requesting the stable-check * endpoint on WordPress.org. * + * @param {Object} options an object with cacheDirectoryPath set to the path to the cache directory in ~/.wp-env. * @return {string} The latest stable version of WordPress, like "6.0.1" */ let CACHED_WP_VERSION; -async function getLatestWordPressVersion() { +async function getLatestWordPressVersion( options ) { // Avoid extra network requests. if ( CACHED_WP_VERSION ) { return CACHED_WP_VERSION; } + const cacheOptions = { + workDirectoryPath: options.cacheDirectoryPath, + }; + + // When we can't connect to the internet, we don't want to break wp-env or + // wait for the stable-check result to timeout. + if ( ! ( await canAccessWPORG() ) ) { + const latestVersion = await getCache( + 'latestWordPressVersion', + cacheOptions + ); + if ( ! latestVersion ) { + throw new Error( + 'Could not find the current WordPress version in the cache and the network is not available.' + ); + } + return latestVersion; + } + const versions = await got( 'https://api.wordpress.org/core/stable-check/1.0/' ).json(); @@ -263,6 +298,7 @@ async function getLatestWordPressVersion() { for ( const [ version, status ] of Object.entries( versions ) ) { if ( status === 'latest' ) { CACHED_WP_VERSION = version; + await setCache( 'latestWordPressVersion', version, cacheOptions ); return version; } } From 6b1a9f26b6b305b0ec2b205a39ff3879e263e38a Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 10 Aug 2023 16:16:32 -0700 Subject: [PATCH 2/6] Avoid configuring on start when wp-env is offline --- packages/env/lib/commands/start.js | 17 +++++++++++++---- packages/env/lib/wordpress.js | 1 + 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/env/lib/commands/start.js b/packages/env/lib/commands/start.js index fed05e4f3d16f..2765e9c4e3198 100644 --- a/packages/env/lib/commands/start.js +++ b/packages/env/lib/commands/start.js @@ -28,6 +28,7 @@ const { configureWordPress, setupWordPressDirectories, readWordPressVersion, + canAccessWPORG, } = require( '../wordpress' ); const { didCacheChange, setCache } = require( '../cache' ); const md5 = require( '../md5' ); @@ -77,16 +78,24 @@ module.exports = async function start( { const configHash = md5( config ); const { workDirectoryPath, dockerComposeConfigPath } = config; const shouldConfigureWp = - update || - ( await didCacheChange( CONFIG_CACHE_KEY, configHash, { - workDirectoryPath, - } ) ); + ( update || + ( await didCacheChange( CONFIG_CACHE_KEY, configHash, { + workDirectoryPath, + } ) ) ) && + // Don't reconfigure everything when we can't connect to the internet because + // the majority of update tasks involve connecting to the internet. (Such + // as downloading sources and pulling docker images.) + ( await canAccessWPORG() ); const dockerComposeConfig = { config: dockerComposeConfigPath, log: config.debug, }; + if ( ! ( await canAccessWPORG() ) ) { + spinner.info( 'wp-env is offline' ); + } + /** * If the Docker image is already running and the `wp-env` files have been * deleted, the start command will not complete successfully. Stopping diff --git a/packages/env/lib/wordpress.js b/packages/env/lib/wordpress.js index 2b56faa48d71e..4ff9adf65f832 100644 --- a/packages/env/lib/wordpress.js +++ b/packages/env/lib/wordpress.js @@ -311,5 +311,6 @@ module.exports = { resetDatabase, setupWordPressDirectories, readWordPressVersion, + canAccessWPORG, getLatestWordPressVersion, }; From f24041fd7adc54893f2f799a9eb6b9cd80ac004f Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 10 Aug 2023 16:18:20 -0700 Subject: [PATCH 3/6] Update changelog --- packages/env/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/env/CHANGELOG.md b/packages/env/CHANGELOG.md index b713292dd2359..9b775dc4f7003 100644 --- a/packages/env/CHANGELOG.md +++ b/packages/env/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Enhancement + +- `wp-env` now works offline after the environment has been created. Note that many `wp-env` configuration changes involve internet connectivity and may not work in offline mode. [#53547](https://github.com/WordPress/gutenberg/pull/53547) + ## 8.10.0 (2023-10-18) ### Bug Fix From da42a8bfa0afb90eb688da1a550cf0aa3b5aedaa Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 10 Aug 2023 16:25:00 -0700 Subject: [PATCH 4/6] persist is offline check --- packages/env/lib/wordpress.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/env/lib/wordpress.js b/packages/env/lib/wordpress.js index 4ff9adf65f832..e8c20aa70f215 100644 --- a/packages/env/lib/wordpress.js +++ b/packages/env/lib/wordpress.js @@ -254,8 +254,14 @@ async function readWordPressVersion( coreSource, spinner, debug ) { * * @return {boolean} True if we can connect to WordPress.org, false otherwise. */ +let IS_OFFLINE; async function canAccessWPORG() { - return !! ( await dns.resolve( 'WordPress.org' ).catch( () => {} ) ); + // Avoid situations where some parts of the code think we're offline and others don't. + if ( IS_OFFLINE !== undefined ) { + return IS_OFFLINE; + } + IS_OFFLINE = !! ( await dns.resolve( 'WordPress.org' ).catch( () => {} ) ); + return IS_OFFLINE; } /** From b2e182634d64f14cf56c3d6184762504b91a6508 Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Thu, 10 Aug 2023 16:44:34 -0700 Subject: [PATCH 5/6] Attempt creating the directory during cache write --- packages/env/lib/cache.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/env/lib/cache.js b/packages/env/lib/cache.js index 187a8a24d030a..98eac693b3420 100644 --- a/packages/env/lib/cache.js +++ b/packages/env/lib/cache.js @@ -40,6 +40,9 @@ async function didCacheChange( key, value, options ) { * @param {WPEnvCacheOptions} options Parsing options */ async function setCache( key, value, options ) { + // Make sure the cache directory exists. + await fs.mkdir( options.workDirectoryPath, { recursive: true } ); + const existingCache = await getCacheFile( options ); existingCache[ key ] = value; From f70dcbbde9703660f0efc5a4f9c8da31cc47a73b Mon Sep 17 00:00:00 2001 From: Noah Allen Date: Fri, 11 Aug 2023 17:08:36 -0700 Subject: [PATCH 6/6] Fix a few unit tests by mocking fs more --- packages/env/lib/config/test/config-integration.js | 2 ++ packages/env/lib/test/cache.js | 1 + 2 files changed, 3 insertions(+) diff --git a/packages/env/lib/config/test/config-integration.js b/packages/env/lib/config/test/config-integration.js index 8d3a1a880adac..a6728f91200f1 100644 --- a/packages/env/lib/config/test/config-integration.js +++ b/packages/env/lib/config/test/config-integration.js @@ -15,6 +15,8 @@ jest.mock( 'fs', () => ( { promises: { readFile: jest.fn(), stat: jest.fn().mockResolvedValue( true ), + mkdir: jest.fn(), + writeFile: jest.fn(), }, } ) ); diff --git a/packages/env/lib/test/cache.js b/packages/env/lib/test/cache.js index 63da7ec47d70b..f73222139d8b1 100644 --- a/packages/env/lib/test/cache.js +++ b/packages/env/lib/test/cache.js @@ -18,6 +18,7 @@ jest.mock( 'fs', () => ( { promises: { readFile: jest.fn(), writeFile: jest.fn(), + mkdir: jest.fn(), }, } ) );