wp-cli commands are classes, and yours can be too

A maintenance task run through wp eval-file has no arguments, no help, no progress and no exit code worth checking. Registering it as a command costs about ten lines and gets all four.

class Shop_Reindex_Command
{
    /**
     * ## OPTIONS
     *
     * [--dry-run]
     * : Report what would change without writing.
     */
    public function __invoke( $args, $assoc ) {
        $dry = isset( $assoc['dry-run'] );
        $bar = WP_CLIUtilsmake_progress_bar( 'Indexing', count( $ids ) );

        foreach ( $ids as $id ) { /* ... */ $bar->tick(); }

        $bar->finish();
        WP_CLI::success( 'Done.' );
    }
}

WP_CLI::add_command( 'shop reindex', 'Shop_Reindex_Command' );

The docblock is the help text — wp help shop reindex renders it — which is the one piece of documentation that stays with the code. WP_CLI::error() exits non-zero, which is what makes the command usable from cron or a deploy script without parsing its output.