From c944579ab5418441d78271bb69524a937469249c Mon Sep 17 00:00:00 2001 From: Yoli Hodde Date: Thu, 31 Oct 2024 15:46:25 -0700 Subject: [PATCH 01/14] Updates to descriptions and examples --- src/bin/vip-import-sql.js | 2 +- src/bin/vip-search-replace.js | 28 +++++++++++++++++----------- src/bin/vip.js | 5 ++++- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/bin/vip-import-sql.js b/src/bin/vip-import-sql.js index ad635a7f5..efd8ca481 100755 --- a/src/bin/vip-import-sql.js +++ b/src/bin/vip-import-sql.js @@ -400,7 +400,7 @@ void command( { ) .option( 'in-place', - 'Perform a search and replace operation on a local SQL file, save the results to the file, then import the updated file.' + 'Perform a search and replace operation on the local input file, and save the results to the file prior to import.' ) .option( 'output', diff --git a/src/bin/vip-search-replace.js b/src/bin/vip-search-replace.js index 9eff4b568..1748d1fca 100755 --- a/src/bin/vip-search-replace.js +++ b/src/bin/vip-search-replace.js @@ -12,33 +12,39 @@ const debug = debugLib( '@automattic/vip:bin:vip-search-replace' ); const examples = [ // `search-replace` flag { - usage: 'vip search-replace --search-replace="from,to"', + usage: 'vip search-replace file.sql --search-replace="from,to"', description: - 'Replace instances of with in the provided \n' + - ' * Ensure there are no spaces between your search-replace parameters', + 'Search for every instance of the value "from" in the local input file named "file.sql" and replace it with the value "to".\n' + + ' * Results of the operation output to STDOUT by default.', }, // `in-place` flag { - usage: 'vip search-replace --search-replace="from,to" --in-place', - description: 'Perform Search and Replace explicitly on the provided input file', + usage: 'vip search-replace file.sql --search-replace="from,to" --in-place', + description: + 'Perform the search and replace operation and save the results to the local input file "file.sql".', }, // `output` flag { - usage: 'vip search-replace --search-replace="from,to" --output=""', + usage: 'vip search-replace file.sql --search-replace="from,to" --output=output-file.sql', description: - 'Search and Replace to the specified output file\n' + - ' * Has no effect when the `in-place` flag is used', + 'Perform the search and replace operation and save the results to a local clone of the input file named "output-file.sql".', }, ]; command( { requiredArgs: 1, } ) - .option( 'search-replace', 'Specify the and pairs to be replaced' ) - .option( 'in-place', 'Perform the search and replace explicitly on the input file' ) + .option( + 'search-replace', + 'Pass a string value to search for and a string value to replace it with. Separate the values by a comma only; no spaces (e.g. --search-replace=“from,to”).' + ) + .option( + 'in-place', + 'Save the results of a search and replace operation to the local input file.' + ) .option( 'output', - 'Create a local copy of the file with the completed search and replace operations. Ignored if the command includes --in-place. Accepts a local file path. Defaults to STDOUT.' + 'Save the results of the search and replace operation to a clone of the local input file. Ignored if the command includes --in-place. Accepts a local file path.' ) .examples( examples ) .argv( process.argv, async ( arg, opt ) => { diff --git a/src/bin/vip.js b/src/bin/vip.js index edb7870e1..0a353c7a3 100755 --- a/src/bin/vip.js +++ b/src/bin/vip.js @@ -37,7 +37,10 @@ const runCmd = async function () { .command( 'export', 'Export a copy of data associated with an environment.' ) .command( 'import', 'Import media or SQL database files to an environment.' ) .command( 'logs', 'Get logs from your VIP applications' ) - .command( 'search-replace', 'Perform search and replace tasks on files' ) + .command( + 'search-replace', + 'Search for a string in a local SQL file and replace it with a new string.' + ) .command( 'slowlogs', 'Retrieve MySQL slow query logs from an environment.' ) .command( 'db', "Access an environment's database." ) .command( 'sync', 'Sync the database from production to a non-production environment.' ) From 671473695049b6ee34923cd51db6a8c44aa4c546 Mon Sep 17 00:00:00 2001 From: Yoli Hodde Date: Mon, 4 Nov 2024 14:13:29 -0800 Subject: [PATCH 02/14] Update src/bin/vip-search-replace.js Co-authored-by: Volodymyr Kolesnykov --- src/bin/vip-search-replace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/vip-search-replace.js b/src/bin/vip-search-replace.js index 1748d1fca..f060fdda2 100755 --- a/src/bin/vip-search-replace.js +++ b/src/bin/vip-search-replace.js @@ -36,7 +36,7 @@ command( { } ) .option( 'search-replace', - 'Pass a string value to search for and a string value to replace it with. Separate the values by a comma only; no spaces (e.g. --search-replace=“from,to”).' + 'Pass a string value to search for and a string value to replace it with. Separate the values by a comma only; no spaces (e.g. --search-replace="from,to").' ) .option( 'in-place', From a532b5e6496439ed10e37579782e3d9e6efbcd4b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 12:11:17 +0000 Subject: [PATCH 03/14] build(deps-dev): bump @types/node from 22.8.5 to 22.8.6 Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.8.5 to 22.8.6. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- npm-shrinkwrap.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 503c1c3da..4d93dcfb1 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -3618,9 +3618,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.8.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.5.tgz", - "integrity": "sha512-5iYk6AMPtsMbkZqCO1UGF9W5L38twq11S2pYWkybGHH2ogPUvXWNlQqJBzuEZWKj/WRH+QTeiv6ySWqJtvIEgA==", + "version": "22.8.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz", + "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==", "dependencies": { "undici-types": "~6.19.8" } @@ -15877,9 +15877,9 @@ "dev": true }, "@types/node": { - "version": "22.8.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.5.tgz", - "integrity": "sha512-5iYk6AMPtsMbkZqCO1UGF9W5L38twq11S2pYWkybGHH2ogPUvXWNlQqJBzuEZWKj/WRH+QTeiv6ySWqJtvIEgA==", + "version": "22.8.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz", + "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==", "requires": { "undici-types": "~6.19.8" } From abfb78753fc40fe4096d0d69822dbd2f19d39f30 Mon Sep 17 00:00:00 2001 From: Volodymyr Kolesnykov Date: Sat, 19 Oct 2024 08:43:43 +0300 Subject: [PATCH 04/14] fix(dev-env): verify the container is running before URL scan --- .../dev-environment/dev-environment-lando.ts | 83 +++++++++++-------- 1 file changed, 48 insertions(+), 35 deletions(-) diff --git a/src/lib/dev-environment/dev-environment-lando.ts b/src/lib/dev-environment/dev-environment-lando.ts index f2fe6de24..e2ed66e08 100644 --- a/src/lib/dev-environment/dev-environment-lando.ts +++ b/src/lib/dev-environment/dev-environment-lando.ts @@ -2,8 +2,8 @@ import chalk from 'chalk'; import debugLib from 'debug'; import App, { type ScanResult } from 'lando/lib/app'; import { buildConfig } from 'lando/lib/bootstrap'; -import Lando, { LandoConfig } from 'lando/lib/lando'; -import landoUtils, { AppInfo } from 'lando/plugins/lando-core/lib/utils'; +import Lando, { type LandoConfig } from 'lando/lib/lando'; +import landoUtils, { type AppInfo } from 'lando/plugins/lando-core/lib/utils'; import landoBuildTask from 'lando/plugins/lando-tooling/lib/build'; import { lookup } from 'node:dns/promises'; import { FileHandle, mkdir, rename } from 'node:fs/promises'; @@ -23,6 +23,7 @@ import { DEV_ENVIRONMENT_NOT_FOUND } from '../constants/dev-environment'; import UserError from '../user-error'; import type { NetworkInspectInfo } from 'dockerode'; +import type Landerode from 'lando/lib/docker'; export interface LandoExecOptions { stdio?: string | [ FileHandle, string, string ]; @@ -374,7 +375,7 @@ export async function landoInfo( const reachableServices = app.info.filter( service => service.urls.length ); reachableServices.forEach( service => ( info[ `${ service.service } urls` ] = service.urls ) ); - const health = await checkEnvHealth( lando, instancePath ); + const health = await checkEnvHealth( lando, app ); const frontEndUrl = app.info.find( service => 'nginx' === service.service )?.urls[ 0 ] ?? ''; const extraService = await getExtraServicesConnections( lando, app ); @@ -545,37 +546,65 @@ async function tryResolveDomains( urls: string[] ): Promise< void > { } } +async function getRunningServicesForProject( + docker: Landerode, + project: string +): Promise< string[] > { + const containers = await docker.listContainers( { + filters: { + label: [ `com.docker.compose.project=${ project }` ], + }, + } ); + + return containers + .filter( container => container.State === 'running' ) + .map( container => container.Labels[ 'com.docker.compose.service' ] ); +} + export async function checkEnvHealth( lando: Lando, - instancePath: string + app: App ): Promise< Record< string, boolean > > { const urls: Record< string, string > = {}; const now = new Date(); - const app = await getLandoApplication( lando, instancePath ); + app.urls ??= []; app.info .filter( service => service.urls.length ) .forEach( service => { service.urls.forEach( url => { - urls[ url ] = service.service; + if ( ! /^https?:\/\/(localhost|127\.0\.0\.1):/.exec( url ) ) { + urls[ url ] = service.service; + } } ); } ); const urlsToScan = Object.keys( urls ).filter( url => ! url.includes( '*' ) ); await tryResolveDomains( urlsToScan ); - let scanResults: ScanResult[] = []; - if ( Array.isArray( app.urls ) ) { - scanResults = app.urls; - app.urls.forEach( entry => { - // We use different status codes to see if the service is up. - // We may consider the service is up when Lando considers it is down. - if ( entry.color !== 'red' ) { - urlsToScan.splice( urlsToScan.indexOf( entry.url ), 1 ); - } - } ); - } + app.urls.forEach( entry => { + // We use different status codes to see if the service is up. + // We may consider the service is up when Lando considers it is down. + if ( entry.color !== 'red' ) { + urlsToScan.splice( urlsToScan.indexOf( entry.url ), 1 ); + } + } ); + const runningServices = await getRunningServicesForProject( lando.engine.docker, app.project ); + Object.entries( urls ).forEach( ( [ url, service ] ) => { + if ( ! runningServices.includes( service ) ) { + ( app.urls as ScanResult[] ).push( { + url, + color: 'red', + status: false, + } ); + + debug( 'Service %s is not running, removing %s from the sacn queue', service, url ); + urlsToScan.splice( urlsToScan.indexOf( url ), 1 ); + } + } ); + + let scanResults = app.urls; if ( urlsToScan.length ) { scanResults = scanResults.concat( await app.scanUrls( urlsToScan, { max: 1, waitCodes: [ 502, 504 ] } ) @@ -594,25 +623,9 @@ export async function checkEnvHealth( } export async function isEnvUp( lando: Lando, instancePath: string ): Promise< boolean > { - const now = new Date(); const app = await getLandoApplication( lando, instancePath ); - - const reachableServices = app.info.filter( service => service.urls.length ); - const webUrls = reachableServices - .map( service => service.urls ) - .flat() - .filter( url => ! /^https?:\/\/(localhost|127\.0\.0\.1):/.exec( url ) ); - - await tryResolveDomains( webUrls ); - const scanResult = await app.scanUrls( webUrls, { max: 1, waitCodes: [ 502, 504 ] } ); - const duration = new Date().getTime() - now.getTime(); - debug( 'isEnvUp took %d ms', duration ); - - // If all the URLs are reachable then the app is considered 'up' - return ( - scanResult.length > 0 && - scanResult.filter( result => result.status ).length === scanResult.length - ); + const healthResults = await checkEnvHealth( lando, app ); + return Object.keys( healthResults ).length > 0 && Object.values( healthResults ).every( Boolean ); } export async function landoExec( From bcef19a8109567d0f6566b345f489a5700a706cc Mon Sep 17 00:00:00 2001 From: Volodymyr Kolesnykov Date: Mon, 4 Nov 2024 20:49:15 +0200 Subject: [PATCH 05/14] Update src/lib/dev-environment/dev-environment-lando.ts Co-authored-by: Rinat K --- src/lib/dev-environment/dev-environment-lando.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/dev-environment/dev-environment-lando.ts b/src/lib/dev-environment/dev-environment-lando.ts index e2ed66e08..2f508c3c4 100644 --- a/src/lib/dev-environment/dev-environment-lando.ts +++ b/src/lib/dev-environment/dev-environment-lando.ts @@ -599,7 +599,7 @@ export async function checkEnvHealth( status: false, } ); - debug( 'Service %s is not running, removing %s from the sacn queue', service, url ); + debug( 'Service %s is not running, removing %s from the scan queue', service, url ); urlsToScan.splice( urlsToScan.indexOf( url ), 1 ); } } ); From be3ce2721ddf6a473376d67e9216f3ed6a395b28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:18:28 +0000 Subject: [PATCH 06/14] build(deps-dev): bump @types/node from 22.8.6 to 22.8.7 Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.8.6 to 22.8.7. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- npm-shrinkwrap.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 4d93dcfb1..e66b81bc9 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -3618,9 +3618,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.8.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz", - "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==", + "version": "22.8.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.7.tgz", + "integrity": "sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q==", "dependencies": { "undici-types": "~6.19.8" } @@ -15877,9 +15877,9 @@ "dev": true }, "@types/node": { - "version": "22.8.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.6.tgz", - "integrity": "sha512-tosuJYKrIqjQIlVCM4PEGxOmyg3FCPa/fViuJChnGeEIhjA46oy8FMVoF9su1/v8PNs2a8Q0iFNyOx0uOF91nw==", + "version": "22.8.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.7.tgz", + "integrity": "sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q==", "requires": { "undici-types": "~6.19.8" } From b565dfb980a02e784e491861607fec06d7537c84 Mon Sep 17 00:00:00 2001 From: Yoli Hodde Date: Mon, 11 Nov 2024 13:26:30 -0800 Subject: [PATCH 07/14] Changes as per feedback in progress --- src/bin/vip-import-sql.js | 4 ++-- src/bin/vip-search-replace.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bin/vip-import-sql.js b/src/bin/vip-import-sql.js index efd8ca481..1295ba6b7 100755 --- a/src/bin/vip-import-sql.js +++ b/src/bin/vip-import-sql.js @@ -400,11 +400,11 @@ void command( { ) .option( 'in-place', - 'Perform a search and replace operation on the local input file, and save the results to the file prior to import.' + 'Overwrite the local input file with the results of the search and replace operation prior to import.' ) .option( 'output', - 'Create a local copy of the imported file with the completed search and replace operations. Ignored if the command includes --in-place, or excludes a --search-replace operation. Accepts a local file path.' + 'Clone the local input file and overwrite the clone with the results of the search and replace operation. Accepts a local file path. Overridden by --in-place if both options are run in the same command, and ignored if the command excludes a --search-replace operation.' ) .examples( examples ) .argv( process.argv, async ( arg, opts ) => { diff --git a/src/bin/vip-search-replace.js b/src/bin/vip-search-replace.js index f060fdda2..1114f6e3a 100755 --- a/src/bin/vip-search-replace.js +++ b/src/bin/vip-search-replace.js @@ -40,11 +40,11 @@ command( { ) .option( 'in-place', - 'Save the results of a search and replace operation to the local input file.' + 'Overwrite the local input file with the results of the search and replace operation.' ) .option( 'output', - 'Save the results of the search and replace operation to a clone of the local input file. Ignored if the command includes --in-place. Accepts a local file path.' + 'Clone the local input file and overwrite the clone with the results of the search and replace operation. Accepts a local file path. Overridden by --in-place if both options are run in the same command.' ) .examples( examples ) .argv( process.argv, async ( arg, opt ) => { From a4188879d317fff72d4e6aacb6ccb6830a860f32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:36:29 +0000 Subject: [PATCH 08/14] build(deps-dev): bump @types/node from 22.8.7 to 22.9.0 Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.8.7 to 22.9.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- npm-shrinkwrap.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index e66b81bc9..f214ee89b 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -3618,9 +3618,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "22.8.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.7.tgz", - "integrity": "sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "dependencies": { "undici-types": "~6.19.8" } @@ -15877,9 +15877,9 @@ "dev": true }, "@types/node": { - "version": "22.8.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.7.tgz", - "integrity": "sha512-LidcG+2UeYIWcMuMUpBKOnryBWG/rnmOHQR5apjn8myTQcx3rinFRn7DcIFhMnS0PPFSC6OafdIKEad0lj6U0Q==", + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", "requires": { "undici-types": "~6.19.8" } From e99262d69ff2898a445fffeff4441b23589ae824 Mon Sep 17 00:00:00 2001 From: Volodymyr Kolesnykov Date: Fri, 8 Nov 2024 23:24:23 +0200 Subject: [PATCH 09/14] fix(dev-env): detection of URLs to replace for multisites (#2081) * fix(dev-env): detection of URLs to replace for multisites * fix: replacement takes paths into consideration * refactor: simplify `replaceDomain()` * fix: update `wp_blogs` * Fix test cases - we don't have id's anymore --------- Co-authored-by: Rinat Khaziev --- __tests__/commands/dev-env-sync-sql.ts | 11 ++- src/commands/dev-env-import-sql.ts | 2 +- src/commands/dev-env-sync-sql.ts | 91 ++++++++++++++++--- .../dev-environment/dev-environment-lando.ts | 5 +- 4 files changed, 88 insertions(+), 21 deletions(-) diff --git a/__tests__/commands/dev-env-sync-sql.ts b/__tests__/commands/dev-env-sync-sql.ts index a0392447d..72ad0813a 100644 --- a/__tests__/commands/dev-env-sync-sql.ts +++ b/__tests__/commands/dev-env-sync-sql.ts @@ -34,6 +34,10 @@ describe( 'commands/DevEnvSyncSQLCommand', () => { blogId: 2, homeUrl: 'https://subsite.com', }, + { + blogId: 3, + homeUrl: 'https://another.com/path', + }, ], }, }; @@ -56,7 +60,7 @@ describe( 'commands/DevEnvSyncSQLCommand', () => { it( 'should return a map of search-replace values', () => { const cmd = new DevEnvSyncSQLCommand( app, env, 'test-slug', lando ); cmd.slug = 'test-slug'; - cmd.siteUrls = [ 'test.go-vip.com' ]; + cmd.siteUrls = [ 'http://test.go-vip.com' ]; cmd.generateSearchReplaceMap(); expect( cmd.searchReplaceMap ).toEqual( { 'test.go-vip.com': 'test-slug.vipdev.lndo.site' } ); @@ -65,12 +69,13 @@ describe( 'commands/DevEnvSyncSQLCommand', () => { it( 'should return a map of search-replace values for multisite', () => { const cmd = new DevEnvSyncSQLCommand( app, msEnv, 'test-slug', lando ); cmd.slug = 'test-slug'; - cmd.siteUrls = [ 'test.go-vip.com', 'subsite.com' ]; + cmd.siteUrls = [ 'https://test.go-vip.com', 'http://subsite.com', 'http://another.com/path' ]; cmd.generateSearchReplaceMap(); expect( cmd.searchReplaceMap ).toEqual( { 'test.go-vip.com': 'test-slug.vipdev.lndo.site', - 'subsite.com': 'subsite-com-2.test-slug.vipdev.lndo.site', + 'subsite.com': 'subsite-com.test-slug.vipdev.lndo.site', + 'another.com/path': 'another-com.test-slug.vipdev.lndo.site/path', } ); } ); } ); diff --git a/src/commands/dev-env-import-sql.ts b/src/commands/dev-env-import-sql.ts index c561d3953..3be537500 100644 --- a/src/commands/dev-env-import-sql.ts +++ b/src/commands/dev-env-import-sql.ts @@ -108,7 +108,7 @@ export class DevEnvImportSQLCommand { * Therefore, for the things to work, we have to pretend that stdin is not a TTY :-) */ process.stdin.isTTY = false; - await exec( lando, this.slug, importArg, { stdio: [ fd, 'pipe', 'pipe' ] } ); + await exec( lando, this.slug, importArg, { stdio: [ fd.fd, 'pipe', 'pipe' ] } ); if ( ! this.options.quiet ) { console.log( `${ chalk.green.bold( 'Success:' ) } Database imported.` ); diff --git a/src/commands/dev-env-sync-sql.ts b/src/commands/dev-env-sync-sql.ts index 4de7c557b..554ec731d 100644 --- a/src/commands/dev-env-sync-sql.ts +++ b/src/commands/dev-env-sync-sql.ts @@ -5,7 +5,6 @@ import chalk from 'chalk'; import fs from 'fs'; import Lando from 'lando'; import { pipeline } from 'node:stream/promises'; -import urlLib from 'url'; import { DevEnvImportSQLCommand, DevEnvImportSQLOptions } from './dev-env-import-sql'; import { ExportSQLCommand } from './export-sql'; @@ -18,6 +17,27 @@ import { fixMyDumperTransform, getSqlDumpDetails, SqlDumpType } from '../lib/dat import { makeTempDir } from '../lib/utils'; import { getReadInterface } from '../lib/validations/line-by-line'; +/** + * Replaces the domain in the given URL + * + * @param string str The URL to replace the domain in. + * @param string domain The new domain + * @return The URL with the new domain + */ +const replaceDomain = ( str: string, domain: string ): string => + str.replace( /^([^:]+:\/\/)([^:/]+)/, `$1${ domain }` ); + +/** + * Strips the protocol from the URL + * + * @param string url The URL to strip the protocol from + * @return The URL without the protocol + */ +function stripProtocol( url: string ): string { + const parts = url.split( '//', 2 ); + return parts.length > 1 ? parts[ 1 ] : parts[ 0 ]; +} + /** * Finds the site home url from the SQL line * @@ -27,8 +47,12 @@ import { getReadInterface } from '../lib/validations/line-by-line'; function findSiteHomeUrl( sql: string ): string | null { const regex = `['"](siteurl|home)['"],\\s?['"](.*?)['"]`; const url = sql.match( regex )?.[ 2 ] || ''; - - return urlLib.parse( url ).hostname || null; + try { + new URL( url ); + return url; + } catch { + return null; + } } /** @@ -42,17 +66,17 @@ async function extractSiteUrls( sqlFile: string ): Promise< string[] > { const readInterface = await getReadInterface( sqlFile ); return new Promise( ( resolve, reject ) => { - const domains: Set< string > = new Set(); + const urls: Set< string > = new Set(); readInterface.on( 'line', line => { - const domain = findSiteHomeUrl( line ); - if ( domain ) { - domains.add( domain ); + const url = findSiteHomeUrl( line ); + if ( url ) { + urls.add( url ); } } ); readInterface.on( 'close', () => { - // Soring by length so that longest domains are replaced first - resolve( Array.from( domains ).sort( ( dom1, dom2 ) => dom2.length - dom1.length ) ); + // Soring by length so that longest URLs are replaced first + resolve( Array.from( urls ).sort( ( url1, url2 ) => url2.length - url1.length ) ); } ); readInterface.on( 'error', reject ); @@ -168,7 +192,9 @@ export class DevEnvSyncSQLCommand { this.searchReplaceMap = {}; for ( const url of this.siteUrls ) { - this.searchReplaceMap[ url ] = this.landoDomain; + this.searchReplaceMap[ stripProtocol( url ) ] = stripProtocol( + replaceDomain( url, this.landoDomain ) + ); } const networkSites = this.env.wpSitesSDS?.nodes; @@ -177,12 +203,18 @@ export class DevEnvSyncSQLCommand { for ( const site of networkSites ) { if ( ! site?.blogId || site.blogId === 1 ) continue; - const url = site?.homeUrl?.replace( /https?:\/\//, '' ); - if ( ! url || ! this.searchReplaceMap[ url ] ) continue; + const url = site?.homeUrl; + if ( ! url ) continue; + + const strippedUrl = stripProtocol( url ); + if ( ! this.searchReplaceMap[ strippedUrl ] ) continue; + + const domain = new URL( url ).hostname; + const newDomain = `${ this.slugifyDomain( domain ) }.${ this.landoDomain }`; - this.searchReplaceMap[ url ] = `${ this.slugifyDomain( url ) }-${ site.blogId }.${ - this.landoDomain - }`; + this.searchReplaceMap[ stripProtocol( url ) ] = stripProtocol( + replaceDomain( url, newDomain ) + ); } } @@ -213,6 +245,34 @@ export class DevEnvSyncSQLCommand { await importCommand.run(); } + public async fixBlogsTable(): Promise< void > { + const networkSites = this.env.wpSitesSDS?.nodes; + if ( ! networkSites ) { + return; + } + + const queries: string[] = []; + for ( const site of networkSites ) { + if ( ! site?.blogId || ! site?.homeUrl ) { + continue; + } + + const oldDomain = new URL( site.homeUrl ).hostname; + const newDomain = + site.blogId !== 1 + ? `${ this.slugifyDomain( oldDomain ) }.${ this.landoDomain }` + : this.landoDomain; + + queries.push( + `UPDATE wp_blogs SET domain = '${ newDomain }' WHERE blog_id = ${ Number( site.blogId ) };` + ); + } + + if ( queries.length ) { + await fs.promises.appendFile( this.sqlFile, queries.join( '\n' ) ); + } + } + /** * Sequentially runs the commands to export, search-replace, and import the SQL file * to the local environment @@ -273,6 +333,7 @@ export class DevEnvSyncSQLCommand { } await this.runSearchReplace(); + await this.fixBlogsTable(); console.log( `${ chalk.green( '✓' ) } Search-replace operation is complete` ); } catch ( err ) { const error = err as Error; diff --git a/src/lib/dev-environment/dev-environment-lando.ts b/src/lib/dev-environment/dev-environment-lando.ts index 2f508c3c4..0d6c7090f 100644 --- a/src/lib/dev-environment/dev-environment-lando.ts +++ b/src/lib/dev-environment/dev-environment-lando.ts @@ -6,7 +6,7 @@ import Lando, { type LandoConfig } from 'lando/lib/lando'; import landoUtils, { type AppInfo } from 'lando/plugins/lando-core/lib/utils'; import landoBuildTask from 'lando/plugins/lando-tooling/lib/build'; import { lookup } from 'node:dns/promises'; -import { FileHandle, mkdir, rename } from 'node:fs/promises'; +import { mkdir, rename } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import path, { dirname } from 'node:path'; import { satisfies } from 'semver'; @@ -24,9 +24,10 @@ import UserError from '../user-error'; import type { NetworkInspectInfo } from 'dockerode'; import type Landerode from 'lando/lib/docker'; +import type { StdioOptions } from 'node:child_process'; export interface LandoExecOptions { - stdio?: string | [ FileHandle, string, string ]; + stdio?: StdioOptions; } /** From e0b171a07c306865e07cd46b1f35aee224761c3d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 12:24:27 +0000 Subject: [PATCH 10/14] build(deps-dev): bump nock from 13.5.5 to 13.5.6 Bumps [nock](https://github.com/nock/nock) from 13.5.5 to 13.5.6. - [Release notes](https://github.com/nock/nock/releases) - [Changelog](https://github.com/nock/nock/blob/main/CHANGELOG.md) - [Commits](https://github.com/nock/nock/compare/v13.5.5...v13.5.6) --- updated-dependencies: - dependency-name: nock dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- npm-shrinkwrap.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index f214ee89b..1fbbaa2c5 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -136,7 +136,7 @@ "dockerode": "^4.0.0", "eslint": "^8.35.0", "jest": "^29.7.0", - "nock": "13.5.5", + "nock": "13.5.6", "prettier": "npm:wp-prettier@2.8.5", "rimraf": "6.0.1", "typescript": "^5.2.2" @@ -10103,9 +10103,9 @@ "dev": true }, "node_modules/nock": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.5.tgz", - "integrity": "sha512-XKYnqUrCwXC8DGG1xX4YH5yNIrlh9c065uaMZZHUoeUUINTOyt+x/G+ezYk0Ft6ExSREVIs+qBJDK503viTfFA==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", + "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", "dev": true, "dependencies": { "debug": "^4.1.0", @@ -20602,9 +20602,9 @@ "dev": true }, "nock": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.5.tgz", - "integrity": "sha512-XKYnqUrCwXC8DGG1xX4YH5yNIrlh9c065uaMZZHUoeUUINTOyt+x/G+ezYk0Ft6ExSREVIs+qBJDK503viTfFA==", + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", + "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", "dev": true, "requires": { "debug": "^4.1.0", diff --git a/package.json b/package.json index a76415898..7a4cd413d 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,7 @@ "dockerode": "^4.0.0", "eslint": "^8.35.0", "jest": "^29.7.0", - "nock": "13.5.5", + "nock": "13.5.6", "prettier": "npm:wp-prettier@2.8.5", "rimraf": "6.0.1", "typescript": "^5.2.2" From 4151c6fee7e4f6a63f78a1fefeee795a8d695507 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 12:24:05 +0000 Subject: [PATCH 11/14] build(deps): bump uuid from 11.0.2 to 11.0.3 Bumps [uuid](https://github.com/uuidjs/uuid) from 11.0.2 to 11.0.3. - [Release notes](https://github.com/uuidjs/uuid/releases) - [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md) - [Commits](https://github.com/uuidjs/uuid/compare/v11.0.2...v11.0.3) --- updated-dependencies: - dependency-name: uuid dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- npm-shrinkwrap.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 1fbbaa2c5..db90eba31 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -44,7 +44,7 @@ "socks-proxy-agent": "^5.0.1", "tar": "^7.4.0", "update-notifier": "7.3.1", - "uuid": "11.0.2", + "uuid": "11.0.3", "xdg-basedir": "^4.0.0", "xml2js": "^0.5.0" }, @@ -12634,9 +12634,9 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz", - "integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", + "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -22396,9 +22396,9 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "uuid": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.2.tgz", - "integrity": "sha512-14FfcOJmqdjbBPdDjFQyk/SdT4NySW4eM0zcG+HqbHP5jzuH56xO3J1DGhgs/cEMCfwYi3HQI1gnTO62iaG+tQ==" + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", + "integrity": "sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==" }, "v8-to-istanbul": { "version": "9.1.0", diff --git a/package.json b/package.json index 7a4cd413d..276b0a452 100644 --- a/package.json +++ b/package.json @@ -175,7 +175,7 @@ "socks-proxy-agent": "^5.0.1", "tar": "^7.4.0", "update-notifier": "7.3.1", - "uuid": "11.0.2", + "uuid": "11.0.3", "xdg-basedir": "^4.0.0", "xml2js": "^0.5.0" }, From a3ae91a7555ce020ea46b9c6f0d3bd488debc494 Mon Sep 17 00:00:00 2001 From: Yoli Hodde Date: Mon, 18 Nov 2024 14:53:46 -0800 Subject: [PATCH 12/14] Updates as per feedback --- src/bin/vip-search-replace.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/vip-search-replace.js b/src/bin/vip-search-replace.js index 1114f6e3a..436023206 100755 --- a/src/bin/vip-search-replace.js +++ b/src/bin/vip-search-replace.js @@ -21,7 +21,7 @@ const examples = [ { usage: 'vip search-replace file.sql --search-replace="from,to" --in-place', description: - 'Perform the search and replace operation and save the results to the local input file "file.sql".', + 'Perform the search and replace operation on the local input file "file.sql" and overwrite the file with the results.', }, // `output` flag { @@ -36,7 +36,7 @@ command( { } ) .option( 'search-replace', - 'Pass a string value to search for and a string value to replace it with. Separate the values by a comma only; no spaces (e.g. --search-replace="from,to").' + 'A comma-separated pair of strings that specify the values to search for and replace (e.g. --search-replace="from,to").' ) .option( 'in-place', From 7b7657d5cb3299ed219b6d17e2d916b80ae3a2c9 Mon Sep 17 00:00:00 2001 From: Yoli Hodde Date: Tue, 19 Nov 2024 15:52:47 -0800 Subject: [PATCH 13/14] Update src/bin/vip-import-sql.js Co-authored-by: Terri Ann Swallow --- src/bin/vip-import-sql.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/vip-import-sql.js b/src/bin/vip-import-sql.js index 1295ba6b7..2aa75f6f2 100755 --- a/src/bin/vip-import-sql.js +++ b/src/bin/vip-import-sql.js @@ -404,7 +404,7 @@ void command( { ) .option( 'output', - 'Clone the local input file and overwrite the clone with the results of the search and replace operation. Accepts a local file path. Overridden by --in-place if both options are run in the same command, and ignored if the command excludes a --search-replace operation.' + 'The local file path to save a copy of the results from the search and replace operation when the --search-replace option is passed. Ignored when used with the --in-place option.' ) .examples( examples ) .argv( process.argv, async ( arg, opts ) => { From 605137adbda9fea0f8844599c61cb9a0f19cc371 Mon Sep 17 00:00:00 2001 From: Yoli Hodde Date: Tue, 19 Nov 2024 16:17:30 -0800 Subject: [PATCH 14/14] Update src/bin/vip-search-replace.js Co-authored-by: Terri Ann Swallow --- src/bin/vip-search-replace.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/vip-search-replace.js b/src/bin/vip-search-replace.js index 436023206..5ab1a1733 100755 --- a/src/bin/vip-search-replace.js +++ b/src/bin/vip-search-replace.js @@ -44,7 +44,7 @@ command( { ) .option( 'output', - 'Clone the local input file and overwrite the clone with the results of the search and replace operation. Accepts a local file path. Overridden by --in-place if both options are run in the same command.' + 'The local file path used to save a copy of the results from the search and replace operation. Ignored when used with the --in-place option.' ) .examples( examples ) .argv( process.argv, async ( arg, opt ) => {