'enqueue_wait_time' => 0,
'queue_max_writes_sec' => 10000,
'max_queue_size_full_sync' => 100000,
'full_sync_send_duration' => HOUR_IN_SECONDS,
Settings::update_settings( $sync_settings );
// Convert comma-delimited string of modules to an array.
if ( ! empty( $assoc_args['modules'] ) ) {
$modules = array_map( 'trim', explode( ',', $assoc_args['modules'] ) );
// Convert the array so that the keys are the module name and the value is true to indicate
// that we want to sync the module.
$modules = array_map( '__return_true', array_flip( $modules ) );
foreach ( array( 'posts', 'comments', 'users' ) as $module_name ) {
'users' === $module_name &&
isset( $assoc_args[ $module_name ] ) &&
'initial' === $assoc_args[ $module_name ]
$modules['users'] = 'initial';
} elseif ( isset( $assoc_args[ $module_name ] ) ) {
$ids = explode( ',', $assoc_args[ $module_name ] );
if ( $ids !== array() ) {
$modules[ $module_name ] = $ids;
if ( empty( $modules ) ) {
if ( Actions::do_full_sync( $modules, 'jetpack_cli' ) ) {
/* translators: %s is a comma separated list of Jetpack modules */
WP_CLI::log( sprintf( __( 'Initialized a new full sync with modules: %s', 'jetpack' ), implode( ', ', array_keys( $modules ) ) ) );
WP_CLI::log( __( 'Initialized a new full sync', 'jetpack' ) );
// Reset sync settings to original.
Settings::update_settings( $original_settings );
/* translators: %s is a comma separated list of Jetpack modules */
WP_CLI::error( sprintf( __( 'Could not start a new full sync with modules: %s', 'jetpack' ), implode( ', ', $modules ) ) );
WP_CLI::error( __( 'Could not start a new full sync', 'jetpack' ) );
// Keep sending to WPCOM until there's nothing to send.
$result = Actions::$sender->do_full_sync();
if ( is_wp_error( $result ) ) {
$queue_empty_error = ( 'empty_queue_full_sync' === $result->get_error_code() );
if ( ! $queue_empty_error || ( $queue_empty_error && ( 1 === $i ) ) ) {
/* translators: %s is an error code */
WP_CLI::error( sprintf( __( 'Sync errored with code: %s', 'jetpack' ), $result->get_error_code() ) );
WP_CLI::log( __( 'Sent data to WordPress.com', 'jetpack' ) );
WP_CLI::log( __( 'Sent more data to WordPress.com', 'jetpack' ) );
// Immediate Full Sync does not wait for WP.com to process data so we need to enforce a wait.
if ( Modules::get_module( 'full-sync' ) instanceof \Automattic\Jetpack\Sync\Modules\Full_Sync_Immediately ) {
} while ( $result && ! is_wp_error( $result ) );
// Reset sync settings to original.
Settings::update_settings( $original_settings );
WP_CLI::success( __( 'Finished syncing to WordPress.com', 'jetpack' ) );
* List the contents of a specific Jetpack sync queue.
* peek : List the 100 front-most items on the queue.
* wp jetpack sync_queue full_sync peek
* @synopsis <incremental|full_sync> <peek>
* @param array $args Positional args.
public function sync_queue( $args ) {
if ( ! Actions::sync_allowed() ) {
WP_CLI::error( __( 'Jetpack sync is not currently allowed for this site.', 'jetpack' ) );
$queue_name = isset( $args[0] ) ? $args[0] : 'sync';
$action = isset( $args[1] ) ? $args[1] : 'peek';
// We map the queue name that way we can support more friendly queue names in the commands, but still use
// the queue name that the code expects.
$queue_name_map = $allowed_queues;
$mapped_queue_name = isset( $queue_name_map[ $queue_name ] ) ? $queue_name_map[ $queue_name ] : $queue_name;
$queue = new Queue( $mapped_queue_name );
$items = $queue->peek( 100 );
/* translators: %s is the name of the queue, either 'incremental' or 'full' */
WP_CLI::log( sprintf( __( 'Nothing is in the queue: %s', 'jetpack' ), $queue_name ) );
foreach ( $items as $item ) {
'args' => wp_json_encode( $item[1] ),
'current_user_id' => $item[2],
'importing' => (string) $item[4],
WP_CLI\Utils\format_items(
* Cancel's the current Jetpack plan granted by this partner, if applicable
* Returns success or error JSON
* : JSON blob of WPCOM API token
* [--partner_tracking_id=<partner_tracking_id>]
* : This is an optional ID that a host can pass to help identify a site in logs on WordPress.com
* @synopsis <token_json> [--partner_tracking_id=<partner_tracking_id>]
* @param array $args Positional args.
* @param array $named_args Named args.
public function partner_cancel( $args, $named_args ) {
list( $token_json ) = $args;
$token = $token_json ? json_decode( $token_json ) : null;
/* translators: %s is the invalid JSON string */
$this->partner_provision_error( new WP_Error( 'missing_access_token', sprintf( __( 'Invalid token JSON: %s', 'jetpack' ), $token_json ) ) );
if ( isset( $token->error ) ) {
$this->partner_provision_error( new WP_Error( $token->error, $token->message ) );
if ( ! isset( $token->access_token ) ) {
$this->partner_provision_error( new WP_Error( 'missing_access_token', __( 'Missing or invalid access token', 'jetpack' ) ) );
if ( Identity_Crisis::validate_sync_error_idc_option() ) {
$this->partner_provision_error(
esc_html__( 'Can not cancel a plan while in safe mode. See: https://jetpack.com/support/safe-mode/', 'jetpack' )
$site_identifier = Jetpack_Options::get_option( 'id' );
if ( ! $site_identifier ) {
$site_identifier = $status->get_site_suffix();
'Authorization' => 'Bearer ' . $token->access_token,
'Host' => 'public-api.wordpress.com',
$url = sprintf( '%s/rest/v1.3/jpphp/%s/partner-cancel', $this->get_api_host(), $site_identifier );
if ( ! empty( $named_args ) && ! empty( $named_args['partner_tracking_id'] ) ) {
$url = esc_url_raw( add_query_arg( 'partner_tracking_id', $named_args['partner_tracking_id'], $url ) );
$result = Client::_wp_remote_request( $url, $request );
if ( is_wp_error( $result ) ) {
$this->partner_provision_error( $result );
WP_CLI::log( wp_remote_retrieve_body( $result ) );
* Provision a site using a Jetpack Partner license
* : JSON blob of WPCOM API token
* : Slug of the requested plan, e.g. premium
* [--wpcom_user_id=<user_id>]
* : WordPress.com ID of user to connect as (must be whitelisted against partner key)
* [--wpcom_user_email=<wpcom_user_email>]
* : Override the email we send to WordPress.com for registration
* [--force_register=<register>]
* : Whether to force a site to register
* [--force_connect=<force_connect>]
* : Force JPS to not reuse existing credentials
* [--home_url=<home_url>]
* : Overrides the home option via the home_url filter, or the WP_HOME constant
* [--site_url=<site_url>]
* : Overrides the siteurl option via the site_url filter, or the WP_SITEURL constant
* [--partner_tracking_id=<partner_tracking_id>]
* : This is an optional ID that a host can pass to help identify a site in logs on WordPress.com
* $ wp jetpack partner_provision '{ some: "json" }' premium 1
* @synopsis <token_json> [--wpcom_user_id=<user_id>] [--plan=<plan_name>] [--force_register=<register>] [--force_connect=<force_connect>] [--home_url=<home_url>] [--site_url=<site_url>] [--wpcom_user_email=<wpcom_user_email>] [--partner_tracking_id=<partner_tracking_id>]
* @param array $args Positional args.
* @param array $named_args Named args.
public function partner_provision( $args, $named_args ) {
list( $token_json ) = $args;
$token = $token_json ? json_decode( $token_json ) : null;
/* translators: %s is the invalid JSON string */
$this->partner_provision_error( new WP_Error( 'missing_access_token', sprintf( __( 'Invalid token JSON: %s', 'jetpack' ), $token_json ) ) );
if ( isset( $token->error ) ) {
$message = isset( $token->message )
$this->partner_provision_error( new WP_Error( $token->error, $message ) );
if ( ! isset( $token->access_token ) ) {
$this->partner_provision_error( new WP_Error( 'missing_access_token', __( 'Missing or invalid access token', 'jetpack' ) ) );
require_once JETPACK__PLUGIN_DIR . '_inc/class.jetpack-provision.php';
$body_json = Jetpack_Provision::partner_provision( $token->access_token, $named_args );
if ( is_wp_error( $body_json ) ) {
'error_code' => $body_json->get_error_code(),
'error_message' => $body_json->get_error_message(),
WP_CLI::log( wp_json_encode( $body_json ) );
* Manages your Jetpack sitemap
* rebuild : Rebuild all sitemaps
* --purge : if set, will remove all existing sitemap data before rebuilding
* --monitor : if set, will output elapsed time, peak memory usage, CPU time (user/system), and average CPU utilization
* --suspend-cache-addition : if set, will suspend cache additions during sitemap generation
* wp jetpack sitemap rebuild
* wp jetpack sitemap rebuild --monitor
* @synopsis <rebuild> [--purge] [--monitor] [--suspend-cache-addition]
* @param array $args Positional args.
* @param array $assoc_args Named args.
public function sitemap( $args, $assoc_args ) {
if ( ! Jetpack::is_module_active( 'sitemaps' ) ) {
WP_CLI::error( __( 'Jetpack Sitemaps module is not currently active. Activate it first if you want to work with sitemaps.', 'jetpack' ) );
if ( ! class_exists( 'Jetpack_Sitemap_Builder' ) ) {
WP_CLI::error( __( 'Jetpack Sitemaps module is active, but unavailable. This can happen if your site is set to discourage search engine indexing. Please enable search engine indexing to allow sitemap generation.', 'jetpack' ) );
if ( isset( $assoc_args['suspend-cache-addition'] ) && $assoc_args['suspend-cache-addition'] ) {
add_filter( 'jetpack_sitemap_suspend_cache_addition', '__return_true' );
WP_CLI::success( 'Suspending cache addition.' );
$monitor = isset( $assoc_args['monitor'] ) && $assoc_args['monitor'];
$start_time = microtime( true );
$rusage_start = function_exists( 'getrusage' ) ? getrusage() : null;
if ( isset( $assoc_args['purge'] ) && $assoc_args['purge'] ) {
$librarian = new Jetpack_Sitemap_Librarian();
$librarian->delete_all_stored_sitemap_data();
// Clear sitemap-related transients
delete_transient( 'jetpack_news_sitemap_xml' );
delete_transient( 'jetpack-sitemap-state-lock' );
WP_CLI::success( __( 'Purged all sitemap data and cleared sitemap transients.', 'jetpack' ) );
$sitemap_builder = new Jetpack_Sitemap_Builder();
$sitemap_builder->update_sitemap();
WP_CLI::success( __( 'Sitemap rebuilt successfully.', 'jetpack' ) );
if ( $monitor && isset( $start_time ) ) {
$end_time = microtime( true );
$peak_memory = memory_get_peak_usage();
$elapsed_time = $end_time - $start_time;
$rusage_end = function_exists( 'getrusage' ) ? getrusage() : null;
WP_CLI::log( '----------------------------------' );
WP_CLI::log( __( 'Performance Metrics:', 'jetpack' ) );
/* translators: %s is a float representing seconds */
WP_CLI::log( sprintf( __( 'Elapsed Time: %.4f seconds', 'jetpack' ), $elapsed_time ) );
/* translators: %s is a human-readable memory size (e.g., 128MB) */
WP_CLI::log( sprintf( __( 'Peak Memory Usage: %s', 'jetpack' ), size_format( $peak_memory ) ) );
if ( ! empty( $rusage_start ) && ! empty( $rusage_end ) ) {
$user_cpu_time = ( $rusage_end['ru_utime.tv_sec'] * 1e6 + $rusage_end['ru_utime.tv_usec'] ) - ( $rusage_start['ru_utime.tv_sec'] * 1e6 + $rusage_start['ru_utime.tv_usec'] );
$system_cpu_time = ( $rusage_end['ru_stime.tv_sec'] * 1e6 + $rusage_end['ru_stime.tv_usec'] ) - ( $rusage_start['ru_stime.tv_sec'] * 1e6 + $rusage_start['ru_stime.tv_usec'] );
/* translators: %d is an integer representing microseconds */
WP_CLI::log( sprintf( __( 'CPU time (user): %d microseconds', 'jetpack' ), $user_cpu_time ) );
/* translators: %d is an integer representing microseconds */
WP_CLI::log( sprintf( __( 'CPU time (system): %d microseconds', 'jetpack' ), $system_cpu_time ) );
// Average CPU utilization over the elapsed wall time.
$total_cpu_sec = ( $user_cpu_time + $system_cpu_time ) / 1e6;
$avg_cpu_pct = $elapsed_time > 0 ? ( $total_cpu_sec / $elapsed_time ) * 100 : 0.0;
/* translators: %s is a percentage like 83.4 */
WP_CLI::log( sprintf( __( 'Average CPU Utilization: %.1f%%', 'jetpack' ), $avg_cpu_pct ) );
WP_CLI::log( '----------------------------------' );
* Allows authorizing a user via the command line and will activate
* wp jetpack authorize_user --token=123456789abcdef
* @synopsis --token=<value>
* @param array $args Positional args.
* @param array $named_args Named args.
public function authorize_user( $args, $named_args ) {
if ( ! is_user_logged_in() ) {
WP_CLI::error( __( 'Please select a user to authorize via the --user global argument.', 'jetpack' ) );
if ( empty( $named_args['token'] ) ) {
WP_CLI::error( __( 'A non-empty token argument must be passed.', 'jetpack' ) );
$is_connection_owner = ! Jetpack::connection()->has_connected_owner();
$current_user_id = get_current_user_id();
( new Tokens() )->update_user_token( $current_user_id, sprintf( '%s.%d', $named_args['token'], $current_user_id ), $is_connection_owner );
WP_CLI::log( wp_json_encode( $named_args ) );
if ( $is_connection_owner ) {
* Auto-enable SSO module for new Jetpack Start connections
* @param bool $enable_sso Whether to enable the SSO module. Default to true.
$enable_sso = apply_filters( 'jetpack_start_enable_sso', true );
Jetpack::handle_post_authorization_actions( $enable_sso, false );
/* translators: %d is a user ID */
WP_CLI::success( sprintf( __( 'Authorized %d and activated default modules.', 'jetpack' ), $current_user_id ) );
/* translators: %d is a user ID */
WP_CLI::success( sprintf( __( 'Authorized %d.', 'jetpack' ), $current_user_id ) );
* Allows calling a WordPress.com API endpoint using the current blog's token.
* : The resource to call with the current blog's token, where `%d` represents the current blog's ID.
* [--api_version=<api_version>]
* : The API version to query against.
* [--base_api_path=<base_api_path>]
* : The base API path to query.
* : A JSON encoded string representing arguments to send in the body.
* : Any number of arguments that should be passed to the resource.
* : Will pretty print the results of a successful API call.
* : Will remove the green success label from successful API calls.
* wp jetpack call_api --resource='/sites/%d'
* @param array $args Positional args.
* @param array $named_args Named args.
public function call_api( $args, $named_args ) {
if ( ! Jetpack::is_connection_ready() ) {
WP_CLI::error( __( 'Jetpack is not currently connected to WordPress.com', 'jetpack' ) );
// Get args that should be passed to resource.
$other_args = array_diff_key( $named_args, array_flip( $consumed_args ) );
$decoded_body = ! empty( $named_args['body'] )
? json_decode( $named_args['body'], true )
$resource_url = ( ! str_contains( $named_args['resource'], '%d' ) )
? $named_args['resource']
: sprintf( $named_args['resource'], Jetpack_Options::get_option( 'id' ) );