Skip to content

Commit

Permalink
wp-env: Fix errors which prevent it from working without internet (#5…
Browse files Browse the repository at this point in the history
…3547)

wp-env should now work without an internet connection

* Cache WordPress version and use it when WordPress.org is unavailable
* Avoid configuring on start when wp-env is offline
* Persist is offline check
  • Loading branch information
noahtallen authored Oct 19, 2023
1 parent 4b6d9f5 commit 9287023
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 6 deletions.
4 changes: 4 additions & 0 deletions packages/env/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions packages/env/lib/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
17 changes: 13 additions & 4 deletions packages/env/lib/commands/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const {
configureWordPress,
setupWordPressDirectories,
readWordPressVersion,
canAccessWPORG,
} = require( '../wordpress' );
const { didCacheChange, setCache } = require( '../cache' );
const md5 = require( '../md5' );
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/env/lib/config/parse-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.'
Expand Down
2 changes: 2 additions & 0 deletions packages/env/lib/config/test/config-integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ jest.mock( 'fs', () => ( {
promises: {
readFile: jest.fn(),
stat: jest.fn().mockResolvedValue( true ),
mkdir: jest.fn(),
writeFile: jest.fn(),
},
} ) );

Expand Down
1 change: 1 addition & 0 deletions packages/env/lib/test/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jest.mock( 'fs', () => ( {
promises: {
readFile: jest.fn(),
writeFile: jest.fn(),
mkdir: jest.fn(),
},
} ) );

Expand Down
45 changes: 44 additions & 1 deletion packages/env/lib/wordpress.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -243,26 +249,62 @@ 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.
*/
let IS_OFFLINE;
async function canAccessWPORG() {
// 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;
}

/**
* 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();

for ( const [ version, status ] of Object.entries( versions ) ) {
if ( status === 'latest' ) {
CACHED_WP_VERSION = version;
await setCache( 'latestWordPressVersion', version, cacheOptions );
return version;
}
}
Expand All @@ -275,5 +317,6 @@ module.exports = {
resetDatabase,
setupWordPressDirectories,
readWordPressVersion,
canAccessWPORG,
getLatestWordPressVersion,
};

0 comments on commit 9287023

Please sign in to comment.