Private plugin updating – GitHub zip file changes the plugin directory (with release or branch name)

It should be possible to carefully adjust the destination path via the upgrader_package_options filter in WordPress, before the upgrader installs the new version of it.

Here’s a barebone example that appends the plugin’s current directory name to the destination path, if it’s same as WP_PLUGIN_DIR (see check in core here), when the package url is a github.com domain:

add_filter( 'upgrader_package_options', function( $options ) {

    $destination = $options['destination'] ?? '';
    $package     = $options['package'] ?? '';
    $dirname     = isset( $options['hook_extra']['plugin'] ) 
                   ? dirname( $options['hook_extra']['plugin'] )
                   : '';
    
    if ( empty( $dirname ) || '.' === $dirname ) {
        return $options;
    }

    if ( 'github.com' !== parse_url( $package, PHP_URL_HOST) ) {
       return $options;
    }
    
    if ( WP_PLUGIN_DIR === $destination ) { 
        $options['destination'] = path_join( $destination, $dirname );
    }

    return $options;
});