Introduction

If, like me, you enjoy writing custom plugins for WordPress, you may know the daunting feeling of managing the versions of your plugins across many websites. While you can use a PHP package manager like Composer for your plugins, this isn’t appropriate for most WordPress deployments. For most, using WordPress’ interface to install and update plugins is the norm. That leaves you two options; get your plugin added to WordPress’ repositories or host it yourself. This post will focus on the latter, showing you how to set up your plugin to automatically detect when new versions are available and install them directly from GitHub.

A screenshot of the PrismPress plugin on a WordPress plugin screen. The plugin shows a notice that a new version is available.
A screenshot of the PrismPress plugin on a WordPress plugin screen. The plugin shows a notice that a new version is available.
A screenshot of the PrismPress plugin on a WordPress plugin screen. The plugin has a notice that states it's updating.
A screenshot of the PrismPress plugin on a WordPress plugin screen. The plugin has a notice that states it’s updating.
A screenshot of the PrismPress plugin on a WordPress plugin screen. The plugin has a notice that states it's updated.
A screenshot of the PrismPress plugin on a WordPress plugin screen. The plugin has a notice that states it’s updated.

Before You Start

Before starting with getting this set up, you should first make sure that you have all of the following:

  • A working WordPress plugin. This should be installed and activated in WordPress without errors.
  • A public GitHub repository for the plugin which contains the source code.

This post will also assume you have a working understanding of GitHub, PHP and WordPress.

Finally, I’d recommend you read through the full post before trying to follow along with the advice.

How Does WordPress Update Plugins?

The WordPress Repository

For most plugins, the WordPress repository takes care of everything. Developers will send their plugins for approval by the WordPress team and, once allowed, their plugins will go onto the marketplace. This approach isn’t for everyone though. To maintain the quality of the community software available on WordPress, developers must adhere to various standards and best practices in their plugins. Failure to comply with and maintain their plugins to these standards will result in them being removed from the repository.

Self Hosting

For self-hosted, updateable plugins, things are slightly different. While you get more freedom in your code, you must supply the code that handles the updating. This can be as complicated or as simple as you want. For some, however, this can be daunting.

The approach is actually quite simple. There are a few parts to this, but this is the full process:

  • In your plugin’s code:
    • With PHP, use the pre_set_site_transient_update_plugins hook to call a function.
    • In this function, send a network request to check the latest version of the plugin.
    • Compare the latest version to the current version and, if it’s newer than what’s installed, return the details of the latest version.
    • Create a JSON file that contains information about the latest plugin version.
  • In your GitHub repository:
    • Create a new release for the latest plugin version.
    • Upload the installable .zip file into the latest release.

You don’t have to use GitHub for this, but GitHub will be used for this demonstration.

How To Make Your Plugin Update In WordPress

I’m going to be using my plugin PrismPress. PrismPress is a lightweight Gutenberg block which uses Prism.js for code syntax highlighting. It’s what I’m using in this very article! I built it after being unable to find another plugin out there that offered syntax highlighting without all of the unnecessary admin screens and settings.

The social share image for the jmwhitworth/PrismPress Github repository
The social share image for the jmwhitworth/PrismPress Github repository

You can see the repository for it here for referencing the code.

Step 1: Create JSON File Detailing Your Plugin’s Version

In the main folder of your plugin, you will need to create update-info.json.

This doesn’t have to be in the main folder, but this guide will assume it is. If you choose to place this file somewhere else, you must account for that moving forward.

For PrismPress at the time of writing this post, it looks as follows:

{
    "new_version": "1.3.1",
    "url": "https://github.com/jmwhitworth/PrismPress",
    "package": "https://github.com/jmwhitworth/PrismPress/releases/download/v1.3.1/prismpress.zip"
}

You’ll see that this has 3 keys:

  • new_version: This should represent the current version of the plugin.
  • url: This is a URL from which you can read about the plugin.
  • package: This should point to a ZIP file which is installable for WordPress. I’ll cover how we get this URL in step 4.

Step 2: Create The Callback Function For Checking Updates

You can do this in any PHP file within the plugin. For PrismPress, I just put this into the main entry file. The main entry file in my example is prismpress.php.

Here’s the full code for the callback function, using the pre_set_site_transient_update_plugins hook:

/**
 * Plugin updater handler function.
 * Pings the Github repo that hosts the plugin to check for updates.
 */
function prismpress_check_for_plugin_update( $transient ) {
    // If no update transient or transient is empty, return.
    if ( empty( $transient->checked ) ) {
        return $transient;
    }

    // Plugin slug, path to the main plugin file, and the URL of the update server
    $plugin_slug = 'prismpress/prismpress.php';
    $update_url = 'https://raw.githubusercontent.com/jmwhitworth/PrismPress/refs/heads/main/update-info.json';

    // Fetch update information from your server
    $response = wp_remote_get( $update_url );
    if ( is_wp_error( $response ) ) {
        return $transient;
    }

    // Parse the JSON response (update_info.json must return the latest version details)
    $update_info = json_decode( wp_remote_retrieve_body( $response ) );

    // If a new version is available, modify the transient to reflect the update
    if ( version_compare( $transient->checked[ $plugin_slug ], $update_info->new_version, '<' ) ) {
        $plugin_data = array(
            'slug'        => 'prismpress',
            'plugin'      => $plugin_slug,
            'new_version' => $update_info->new_version,
            'url'         => $update_info->url,
            'package'     => $update_info->package, // URL of the plugin zip file
        );
        $transient->response[ $plugin_slug ] = (object) $plugin_data;
    }

    return $transient;
}
add_filter( 'pre_set_site_transient_update_plugins', 'prismpress_check_for_plugin_update' );

To use this code, you’ll need to make the following tweaks:

  • On line 12, you’ll see the $plugin_slug is prismpress/prismpress.php. This is the plugin’s folder name followed by the main entry file.
  • On line 13, $update_url is https://raw.githubusercontent.com/jmwhitworth/PrismPress/refs/heads/main/update-info.json. This will point to the update-info.json file we created above.
  • On line 27, change the slug to match your plugin. For my example, this was just prismpress.

The code is well commented, but here’s a quick run-through for those who want a full understanding of what’s happening:

  1. First, this checks if the update transient has already been checked. If it has, it skips.
  2. We declare the location of the main plugin file and the remote file which houses the information about the latest version.
  3. The remote file is fetched via wp_remote_get, retrieving the content. If the fetch is successful, it’s converted into a useable object.
  4. We compare the version of the remote file to that of our main plugin file.
  5. If the remote file is a higher version, we store this as a response in the transient.
  6. The transient is returned. WordPress handles the rest from here and will show the user that an update is available.

Step 3: Create A ZIP Of Your Plugin

This will vary depending on your operating system, but as a Windows user, this is how I zip my code. You will need to look this up for your own platform if you don’t already know how to do so.

In File Explorer, I’ve navigated to the folder containing all my plugin’s code. This should perfectly match what is on the GitHub repository.

By highlighting all of the files and then right-clicking, I can choose to compress everything into a ZIP file:

A screenshot of the Windows File Explorer open in a folder containing WordPress plugin files. The files are all highlighted and the option to compress them as a ZIP is highlighted in red.
A screenshot of the Windows File Explorer open in a folder containing WordPress plugin files. The files are all highlighted and the option to compress them as a ZIP is highlighted in red.

This creates the ZIP file, but you must rename it to match the name of the plugin’s folder in WordPress. Here you can see that mine is now called prismpress.zip:

A screenshot of the Windows File Explorer open in a folder containing WordPress plugin files. There's a zipped file at the bottom called 'prismpress.zip'
A screenshot of the Windows File Explorer open in a folder containing WordPress plugin files. There’s a zipped file at the bottom called ‘prismpress.zip’

There are a few essential things that you must ensure:

  1. The name of the ZIP file must match the name of the plugin’s folder in WordPress. When WordPress unzips and installs this, the resulting folder will take on the name of the .zip file. If the name is different, this will cause the plugin to get deactivated and will break the file locations we gave in step 2.
  2. The file must directly contain the plugin files, with no nested parent folder. By this, I mean that when it’s extracted, a folder sharing the same name should appear, and this should contain all of the plugin files. This is how WordPress expects the files to be structured. If this is not done correctly, auto-updating will not work.

Step 4: Creating A Release on GitHub

Now that the plugin has the required files for handling updates, we need to deploy it to GitHub. If you load up your GitHub repository for the plugin, you’ll see a ‘Releases’ option on the right-hand side of the page:

A screenshot of the PrismPress GitHub page. The 'Releases' option on the right-hand side is circled in red.
A screenshot of the PrismPress GitHub page. The ‘Releases’ option on the right-hand side is circled in red.

On the Releases page, you can then ‘Draft a new release’:

A screenshot of a GitHub Releases page. The 'Draft a new release' button is highlighted in red.
A screenshot of a GitHub Releases page. The ‘Draft a new release’ button is highlighted in red.

You can then create a tag, title and description.

You must then upload your zipped plugin file by using the ‘Attach binaries by dropping them here or selecting them’ field.

Once your ZIP is uploaded, you should have something like this (minus the ‘Duplicate tag name’, as I was recreating something that already existed for this demo):

A screenshot of the GitHub create a release screen. A tag, title and description have been entered and a ZIP file has been attached.
A screenshot of the GitHub create a release screen. A tag, title and description have been entered and a ZIP file has been attached.

Finally, here’s the last piece of the puzzle. With your release created, you’ll see your ZIP file is downloadable. In update-info.json, the package field needs to point to a downloadable ZIP of your plugin. This is what that URL is. You can get it by right-clicking and copying the link:

A screenshot of the GitHub releases screen. A ZIP file for the release has been right-clicked, and the 'Copy Link' option is highlighted in red.
A screenshot of the GitHub releases screen. A ZIP file for the release has been right-clicked, and the ‘Copy Link’ option is highlighted in red.

While it’s a little bit like a chicken-or-the-egg situation, as you can’t really make the release without first having the URL ready, this is only a small issue the first time you go to do this. Once you’ve got the URL, all you’ll need to do is increment it to match the latest version and, so long as your naming is consistent, you’ll have the correct URL each time.

For instance, my URL is https://github.com/jmwhitworth/PrismPress/releases/download/v1.3.1/prismpress.zip. If my next release is version 1.4.0, I’ll know that the URL will have to be https://github.com/jmwhitworth/PrismPress/releases/download/v1.4.0/prismpress.zip. And, so long as I call the release v1.4.0, and upload the prismpress.zip file with the right name, this URL will resolve correctly.

Conclusion

While it might seem daunting, this whole process is actually quite simple. Once you’ve done it once, it’s all clear and easy to understand.

The great thing about using GitHub is its availability and reliability. It removes the fear that changing your server’s URL could prevent updates from happening or the fear of downtime affecting service.

While it might be easier for some to upload to the WordPress repositories, for my smaller plugins that are only on a handful of websites I manage, this makes sense.

I hope you found this useful. If you have any thoughts or questions, let me know by leaving a comment below.

Leave a Reply

Your email address will not be published. Required fields are marked *