Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# wp-cli-move

Sync your WordPress content (database and uploads) between stages using the power of WP-CLI aliases.
Sync your WordPress content (database, uploads, themes, plugins, mu-plugins, languages) between stages using the power of WP-CLI aliases.

## Install

Expand Down Expand Up @@ -47,17 +47,17 @@ For more information about alias configuration, refer to the following WP-CLI do
Depending on the sync direction, use either the `pull` or `push` commands.

```sh
wp move pull/push [<alias>] [--db] [--uploads] [--disable-compress] [--dry-run]
wp move pull/push [<alias>] [--db] [--uploads] [--themes] [--plugins] [--mu-plugins] [--languages] [--disable-compress] [--dry-run]
```

If you omit the `--db` or `--uploads` flags, both data types will be synced by default.
If you omit the specific flags (--db, --uploads, --themes, --plugins, --mu-plugins, --languages), both database and uploads will be synced by default for backward compatibility.

Note that the `<alias>` argument is optional. Configured aliases will be shown in a menu to choose from if left empty.

> [!CAUTION]
> Just like any tool that manipulates your data, it's **always a good idea to make a backup before running commands**.
>
> Especially when syncing uploads, which uses the `rsync` command with the `--delete` flag under the hood and can wipe all your media files if used incorrectly.
> Especially when syncing content folders (uploads, themes, plugins, etc.), which uses the `rsync` command with the `--delete` flag under the hood and can wipe all your files if used incorrectly.
>
> **Be sure to know what you're doing.**

Expand All @@ -68,6 +68,10 @@ Both `pull` and `push` commands use the same options.
- `[<alias>]`: The alias you want to sync with.
- `--db`: Sync only the database.
- `--uploads`: Sync only the uploads.
- `--themes`: Sync only the themes.
- `--plugins`: Sync only the plugins.
- `--mu-plugins`: Sync only the mu-plugins.
- `--languages`: Sync only the languages.
- `--disable-compress`: Disable database dump compression.
- `--dry-run`: Print the command sequence without making any changes.

Expand Down Expand Up @@ -95,6 +99,32 @@ Push your local content to your staging environment:
wp move push staging
```

### Syncing specific content types

Pull only themes from production:

```sh
wp move pull production --themes
```

Push only plugins to staging:

```sh
wp move push staging --plugins
```

Sync multiple content types at once:

```sh
wp move pull production --themes --plugins --mu-plugins
```

Sync everything (database, uploads, themes, plugins, mu-plugins, and languages):

```sh
wp move pull production --db --uploads --themes --plugins --mu-plugins --languages
```

## Credits

This WP-CLI package aims to replace the (still working but unmaintained) awesome [Wordmove](https://github.com/welaika/wordmove) Ruby gem 💎. It has been a time and life saver for many years. I'll be forever grateful to [@alessandro-fazzi](https://github.com/alessandro-fazzi) for creating it! 🙌
Expand Down
63 changes: 52 additions & 11 deletions src/Model/Alias.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
* port?: string,
* path?: string,
* }
* /**
*/
/**
* @phpstan-import-type alias_config from MoveCommand
*/
final class Alias implements Stringable {
Expand All @@ -45,11 +46,11 @@
* @param integer|null $port
* @param string|null $key
*/
public function __construct(

Check failure on line 49 in src/Model/Alias.php

View workflow job for this annotation

GitHub Actions / code-quality / PHPStan

PHPDoc tag @param for parameter $ssh_bits with type n5s\WpCliMove\Model\ssh_bits|null is not subtype of native type array|null.

Check failure on line 49 in src/Model/Alias.php

View workflow job for this annotation

GitHub Actions / code-quality / PHPStan

Method n5s\WpCliMove\Model\Alias::__construct() has parameter $ssh_bits with no value type specified in iterable type array.
private readonly string $name,
private string $path,
private ?string $url = null,
private readonly ?array $ssh_bits = null,

Check failure on line 53 in src/Model/Alias.php

View workflow job for this annotation

GitHub Actions / code-quality / PHPStan

Property n5s\WpCliMove\Model\Alias::$ssh_bits has unknown class n5s\WpCliMove\Model\ssh_bits as its type.

Check failure on line 53 in src/Model/Alias.php

View workflow job for this annotation

GitHub Actions / code-quality / PHPStan

Parameter $ssh_bits of method n5s\WpCliMove\Model\Alias::__construct() has invalid type n5s\WpCliMove\Model\ssh_bits.

Check failure on line 53 in src/Model/Alias.php

View workflow job for this annotation

GitHub Actions / code-quality / PHPStan

PHPDoc type for property n5s\WpCliMove\Model\Alias::$ssh_bits with type n5s\WpCliMove\Model\ssh_bits|null is not subtype of native type array|null.
private readonly ?string $user = null,
private readonly ?string $host = null,
private readonly ?int $port = null,
Expand Down Expand Up @@ -136,9 +137,9 @@
*/
public function get_url(): string {
if ( ! isset( $this->url ) ) {
$result = $this->run_wp( command: 'config get WP_HOME', quiet: true );
$result = $this->run_wp( 'config get WP_HOME', false, true );
if ( ! $result->is_successful() ) {
$result = $this->run_wp( command: 'option get home', quiet: true );
$result = $this->run_wp( 'option get home', false, true );
}
$this->url = $result->stdout;
}
Expand All @@ -152,7 +153,47 @@
* @return string
*/
public function get_upload_path(): string {
$result = $this->run_wp( command: 'eval "echo wp_get_upload_dir()[\'basedir\'] ?? \'\';"', quiet: true );
$result = $this->run_wp( 'eval "echo wp_get_upload_dir()[\'basedir\'] ?? \'\';"', false, true );
return $result->stdout;
}

/**
* Get the themes path
*
* @return string
*/
public function get_themes_path(): string {
$result = $this->run_wp( 'eval "echo get_theme_root() ?? \'\';"', false, true );
return $result->stdout;
}

/**
* Get the plugins path
*
* @return string
*/
public function get_plugins_path(): string {
$result = $this->run_wp( 'eval "echo WP_PLUGIN_DIR ?? \'\';"', false, true );
return $result->stdout;
}

/**
* Get the mu-plugins path
*
* @return string
*/
public function get_mu_plugins_path(): string {
$result = $this->run_wp( 'eval "echo WPMU_PLUGIN_DIR ?? \'\';"', false, true );
return $result->stdout;
}

/**
* Get the languages path
*
* @return string
*/
public function get_languages_path(): string {
$result = $this->run_wp( 'eval "echo WP_LANG_DIR ?? \'\';"', false, true );
return $result->stdout;
}

Expand Down Expand Up @@ -385,9 +426,9 @@
case self::COMMAND_TYPE_RAW:
/** @var ProcessRun $result */
$result = WP_CLI::launch(
command: $command,
exit_on_error: false,
return_detailed: true
$command,
false,
true
);
break;
default:
Expand All @@ -396,10 +437,10 @@
}

$process_result = new ProcessResult(
command: sprintf( '%s%s', self::COMMAND_TYPE_WP === $type ? 'wp ' : '', $result->command ),
exit_code: $result->return_code,
stdout: $result->stdout,
stderr: $result->stderr,
sprintf( '%s%s', self::COMMAND_TYPE_WP === $type ? 'wp ' : '', $result->command ),
$result->return_code,
$result->stdout,
$result->stderr,
);

if ( ! $quiet ) {
Expand All @@ -424,10 +465,10 @@
$ssh = $config['ssh'] ?? null;
if ( null !== $ssh ) {
/** @var ssh_bits $ssh_bits */
$ssh_bits = Utils\parse_ssh_url( $ssh );

Check failure on line 468 in src/Model/Alias.php

View workflow job for this annotation

GitHub Actions / code-quality / PHPStan

PHPDoc tag @var for variable $ssh_bits contains unknown class n5s\WpCliMove\Model\ssh_bits.
}

$path = $ssh_bits['path'] ?? $config['path'] ?? null;

Check failure on line 471 in src/Model/Alias.php

View workflow job for this annotation

GitHub Actions / code-quality / PHPStan

Access to offset 'path' on an unknown class n5s\WpCliMove\Model\ssh_bits.

if ( ! $path ) {
throw new InvalidArgumentException( 'A path is required' );
Expand All @@ -437,9 +478,9 @@
name: $name,
path: $path,
url: $config['url'] ?? null,
ssh_bits: $ssh_bits ?? null,

Check failure on line 481 in src/Model/Alias.php

View workflow job for this annotation

GitHub Actions / code-quality / PHPStan

Parameter $ssh_bits of class n5s\WpCliMove\Model\Alias constructor expects array|null, n5s\WpCliMove\Model\ssh_bits|null given.
user: $ssh_bits['user'] ?? null,

Check failure on line 482 in src/Model/Alias.php

View workflow job for this annotation

GitHub Actions / code-quality / PHPStan

Access to offset 'user' on an unknown class n5s\WpCliMove\Model\ssh_bits.
host: $ssh_bits['host'] ?? null,

Check failure on line 483 in src/Model/Alias.php

View workflow job for this annotation

GitHub Actions / code-quality / PHPStan

Access to offset 'host' on an unknown class n5s\WpCliMove\Model\ssh_bits.
port: isset( $ssh_bits['port'] ) ? (int) $ssh_bits['port'] : null,
key: $config['key'] ?? null,
);
Expand Down
Loading
Loading