How to extract a .wp-env.json or composer.json containing plugin versions from a production website?

Is there a concise, usual way of extracting theme and plugin versions
into a config file such as .wp-env.json or composer.json without
having direct access to the database?

I assumed there existed a .wp-env.json generator, but I couldn’t find it. That might help if available.

There are tons of ways to clone a site, but let’s assume we only want to get a basic auto-generated .wp-env.json info directly from the main site.

Not sure if it’s useful but here’s a skeleton of a plugin that assumes only plugins and themes hosted on wordpress.org:

<?php
/**
 * Plugin Name: WPSE-407466 - REST endpoint for basic wp-env info
 * Version:     0.0.1
 */
add_action( 'rest_api_init', function() {
    register_rest_route(
        'wpse/v1',
        '/wp-env/',
        array(
           'methods'             => 'GET',
           'permission_callback' => function () {
                return current_user_can( 'manage_options' );
            },
           'callback'            => function( $request ) {
                $active_plugins = (array) get_option( 'active_plugins', array() );
                $data           = array();
                foreach( $active_plugins as $plugin ) {
                   $data['plugins'][] = 'WordPress/' . dirname( $plugin );
                }
                $data['themes']     = 'WordPress/' . get_stylesheet();
                $data['core']       = 'WordPress/WordPress#' . get_bloginfo( 'version' );
                // phpVersion only supports x.y not x.y.z
                $data['phpVersion'] =  sprintf( "%s.%s", PHP_MAJOR_VERSION, PHP_MINOR_VERSION ); 
                return $data;
            },
        )
    );
} );

A GET request (with a wp-rest nonce or application password) on:

https://example.com/wp-json/wpse/v1/wp-env

as logged in user with manage_options capability will output something like:

{
    "plugins": [
        "WordPress/buddypress",
        "WordPress/jetpack"
    ],
    "themes": [
        "WordPress/neve"
    ],
    "core": "WordPress/WordPress#5.9.3",
    "phpVersion": "7.4"
}

but note that this is not tested yet with wp-env, just used

https://developer.wordpress.org/block-editor/reference-guides/packages/packages-env/

as a guidance.

Here’s an example using the application password:

curl --user "USERNAME:APPLICATION-PASSWORD" "https://HOSTNAME/wp-json/wpse/v1/wp-env"

where we replace USERNAME, APPLICATION-PASSWORD and HOSTNAME.

Download link formulas

Let’s add the versions for plugins and themes to generate download links.

If we only have wordpress.org hosted plugins and themes and if we assume they follow this download link formula:

https://downloads.wordpress.org/plugin/<PLUGIN-SLUG>.x.y.z.zip
https://downloads.wordpress.org/theme/<THEME-SLUG>.x.y.z.zip

Then we can update the plugin:

/**
 * Plugin Name: WPSE-407466 - REST endpoint for basic wp-env info
 * Version:     0.0.2
 */
add_action( 'rest_api_init', function() {
    register_rest_route(
        'wpse/v1',
        '/wp-env/',
        array(
           'methods'             => 'GET',
           'permission_callback' => function () {
                return current_user_can( 'manage_options' );
            },
           'callback'            => function( $request ) {
                if ( ! function_exists( 'get_plugins' ) ) {
                    require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
                }
                $active_plugins    = (array) get_option( 'active_plugins', array() );
                $installed_plugins = get_plugins();
                $data              = array();
                foreach( $active_plugins as $plugin ) {
                   $data['plugins'][] = sprintf( 
                        'https://downloads.wordpress.org/plugin/%s.%s.zip',
                        dirname( $plugin ),
                        $installed_plugins[$plugin]['Version']
                    );
                }
                $data['themes'][] = sprintf( 
                    'https://downloads.wordpress.org/theme/%s.%s.zip',
                    get_stylesheet(),
                    wp_get_theme()->Version
                );
                $data['core']       = 'WordPress/WordPress#' . get_bloginfo( 'version' );
                // phpVersion only supports x.y not x.y.z
                $data['phpVersion'] =  sprintf( "%s.%s", PHP_MAJOR_VERSION, PHP_MINOR_VERSION ); 
                return $data;
           }
        )
    );
} );

with output like:

{
    "plugins": [
        "https://downloads.wordpress.org/plugin/buddypress.10.3.0.zip",
        "https://downloads.wordpress.org/plugin/jetpack.10.9.1.zip"
    ],
    "themes": [          
        "https://downloads.wordpress.org/theme/neve.2.9.2.zip"
    ],
    "core": "WordPress/WordPress#5.9.3",
    "phpVersion": "7.4"
}

REST endpoint for plugins

There is also a way to extract info on the active plugins from the plugins REST endpoint.

Here’s an example using the application password:

curl --user "USERNAME:APPLICATION-PASSWORD" "https://HOSTNAME/wp-json/wp/v2/plugins/?status=active"

where we replace USERNAME, APPLICATION-PASSWORD and HOSTNAME.

Example output:

[
   {
      "plugin": "jetpack/jetpack",
      "status": "active",
      "name": "Jetpack",
      "plugin_uri": "https://jetpack.com",
      "author": "Automattic",
      "author_uri": "https://jetpack.com",
      "description": {
        "raw": "Security, performance, and marketing tools made by WordPress experts. Jetpack keeps your site protected so you can focus on more important things.",
        "rendered": "Security, performance, and marketing tools made by WordPress experts. Jetpack keeps your site protected so you can focus on more important things. <cite>By <a href=\"https://jetpack.com\">Automattic</a>.</cite>"
      },
      "version": "10.9.1",
      "network_only": false,
      "requires_wp": "5.9",
      "requires_php": "5.6",
      "textdomain": "jetpack",
      "_links": {
        "self": [
          {
            "href": "https://example.com/wp-json/wp/v2/plugins/jetpack/jetpack"
          }
        ]
      }
    },
    ... etc ...
]

This data could be used in a custom local script.

Get download links from api.wordpress.org

If the formulas above are not always working, then we could otherwise get actual download links from api.wordpress.org with a little more effort.

Here’s more on this wordpress.org API:

https://codex.wordpress.org/WordPress.org_API

The mock test in core had an informative link regarding API parameters.

Here’s the info on the JetPack plugin as an example:

https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slug]=jetpack

that will contain zip download links within the plugin’s version history:

    versions: {
        10.0: "https://downloads.wordpress.org/plugin/jetpack.10.0.zip",
        10.1: "https://downloads.wordpress.org/plugin/jetpack.10.1.zip",
        10.2: "https://downloads.wordpress.org/plugin/jetpack.10.2.zip",
        10.2.1: "https://downloads.wordpress.org/plugin/jetpack.10.2.1.zip",
        10.3: "https://downloads.wordpress.org/plugin/jetpack.10.3.zip",
        10.4: "https://downloads.wordpress.org/plugin/jetpack.10.4.zip",
        10.5: "https://downloads.wordpress.org/plugin/jetpack.10.5.zip",
        10.5.1: "https://downloads.wordpress.org/plugin/jetpack.10.5.1.zip",
        10.6: "https://downloads.wordpress.org/plugin/jetpack.10.6.zip",
        10.7: "https://downloads.wordpress.org/plugin/jetpack.10.7.zip",
        10.8: "https://downloads.wordpress.org/plugin/jetpack.10.8.zip",
        10.9: "https://downloads.wordpress.org/plugin/jetpack.10.9.zip",
        10.9.1: "https://downloads.wordpress.org/plugin/jetpack.10.9.1.zip",
        11.0: "https://downloads.wordpress.org/plugin/jetpack.11.0.zip",
        11.1: "https://downloads.wordpress.org/plugin/jetpack.11.1.zip",
        11.1-a.1: "https://downloads.wordpress.org/plugin/jetpack.11.1-a.1.zip",
        11.1-a.3: "https://downloads.wordpress.org/plugin/jetpack.11.1-a.3.zip",
        11.1-a.5: "https://downloads.wordpress.org/plugin/jetpack.11.1-a.5.zip",
        11.1-beta: "https://downloads.wordpress.org/plugin/jetpack.11.1-beta.zip",
        11.1-beta2: "https://downloads.wordpress.org/plugin/jetpack.11.1-beta2.zip",
        11.2-a.1: "https://downloads.wordpress.org/plugin/jetpack.11.2-a.1.zip",
        ...cut...
        trunk: "https://downloads.wordpress.org/plugin/jetpack.zip"
    },

It’s possible to reduce the size of the response by turning off fields with request[fields][<FIELD>]=0.

As an example let’s turn off the fields for contributors, sections, screenshots, tags, rating and banners:

https://api.wordpress.org/plugins/info/1.2/?action=plugin_information&request[slug]=jetpack&request[fields][contributors]=0&request[fields][sections]=0&request[fields][screenshots]=0&request[fields]plugins,themes,plugins,themes=0&request[fields][ratings]=0&request[fields][banners]=0

with the output:

{
    name: "Jetpack &#8211; WP Security, Backup, Speed, &amp; Growth",
    slug: "jetpack",
    version: "11.1",
    author: "<a href="https://jetpack.com">Automattic</a>",
    author_profile: "https://profiles.wordpress.org/automattic/",
    requires: "5.9",
    tested: "6.0",
    requires_php: "5.6",
    rating: 78,
    num_ratings: 1778,
    support_threads: 248,
    support_threads_resolved: 218,
    active_installs: 5000000,
    last_updated: "2022-07-05 3:29pm GMT",
    added: "2011-01-20",
    homepage: "https://jetpack.com",
    download_link: "https://downloads.wordpress.org/plugin/jetpack.11.1.zip",
    versions: {
        10.0: "https://downloads.wordpress.org/plugin/jetpack.10.0.zip",
        10.1: "https://downloads.wordpress.org/plugin/jetpack.10.1.zip",
        10.2: "https://downloads.wordpress.org/plugin/jetpack.10.2.zip",
        10.2.1: "https://downloads.wordpress.org/plugin/jetpack.10.2.1.zip",
        10.3: "https://downloads.wordpress.org/plugin/jetpack.10.3.zip",
        10.4: "https://downloads.wordpress.org/plugin/jetpack.10.4.zip",
        10.5: "https://downloads.wordpress.org/plugin/jetpack.10.5.zip",
        10.5.1: "https://downloads.wordpress.org/plugin/jetpack.10.5.1.zip",
        10.6: "https://downloads.wordpress.org/plugin/jetpack.10.6.zip",
        10.7: "https://downloads.wordpress.org/plugin/jetpack.10.7.zip",
        10.8: "https://downloads.wordpress.org/plugin/jetpack.10.8.zip",
        10.9: "https://downloads.wordpress.org/plugin/jetpack.10.9.zip",
        10.9.1: "https://downloads.wordpress.org/plugin/jetpack.10.9.1.zip",
        11.0: "https://downloads.wordpress.org/plugin/jetpack.11.0.zip",
        11.1: "https://downloads.wordpress.org/plugin/jetpack.11.1.zip",
        11.1-a.1: "https://downloads.wordpress.org/plugin/jetpack.11.1-a.1.zip",
        11.1-a.3: "https://downloads.wordpress.org/plugin/jetpack.11.1-a.3.zip",
        11.1-a.5: "https://downloads.wordpress.org/plugin/jetpack.11.1-a.5.zip",
        11.1-beta: "https://downloads.wordpress.org/plugin/jetpack.11.1-beta.zip",
        11.1-beta2: "https://downloads.wordpress.org/plugin/jetpack.11.1-beta2.zip",
        11.2-a.1: "https://downloads.wordpress.org/plugin/jetpack.11.2-a.1.zip",
        ...cut...
        trunk: "https://downloads.wordpress.org/plugin/jetpack.zip"
    },
    donate_link: ""
}

Similar for the themes.

Hopefully this can help you to generate your own local config files.