feat: add GitHub-powered updates to WordPress admin
Build plugin release / release (push) Failing after 4s

This commit is contained in:
2026-08-24 09:27:21 +03:30
parent 572a740d46
commit 656fc907a9
6 changed files with 342 additions and 2 deletions
@@ -2,6 +2,8 @@
namespace Dabestaniha\AuthenticateBridge\App\Http\Controllers;
use Dabestaniha\AuthenticateBridge\App\Support\PluginUpdater;
class SettingsPageMenuController extends InvocableController
{
public function __invoke()
@@ -27,7 +29,8 @@ class SettingsPageMenuController extends InvocableController
submit_button();
?>
</form>
<?php PluginUpdater::renderUpdatePanel(); ?>
</div>
<?php
}
}
}
+240
View File
@@ -0,0 +1,240 @@
<?php
namespace Dabestaniha\AuthenticateBridge\App\Support;
final class PluginUpdater
{
private const API_URL = 'https://api.github.com/repos/dabestaniha/mahak-authenticate-bridge/releases/latest';
private const RELEASE_ASSET = 'mahak-authenticate-bridge.zip';
private const RELEASE_CACHE = 'mahak_authenticate_bridge_release';
private const CHECK_ACTION = 'mahak_authenticate_check_updates';
private static ?self $instance = null;
private string $plugin;
private string $slug;
private function __construct()
{
$this->plugin = plugin_basename(MAHAK_AUTHENTICATE_BRIDGE_FILE);
$this->slug = dirname($this->plugin);
}
public static function boot(): void
{
if (self::$instance !== null) {
return;
}
self::$instance = new self();
add_filter('pre_set_site_transient_update_plugins', [self::$instance, 'addUpdate']);
add_filter('plugins_api', [self::$instance, 'addPluginInformation'], 20, 3);
add_action('admin_post_'.self::CHECK_ACTION, [self::$instance, 'checkNow']);
}
public function addUpdate($transient)
{
if (!is_object($transient) || empty($transient->checked)) {
return $transient;
}
$update = $this->availableUpdate();
if ($update !== null) {
if (!isset($transient->response) || !is_array($transient->response)) {
$transient->response = [];
}
$transient->response[$this->plugin] = $update;
}
return $transient;
}
public function addPluginInformation($result, string $action, $args)
{
if ($action !== 'plugin_information' || ($args->slug ?? '') !== $this->slug) {
return $result;
}
$release = $this->latestRelease();
if (is_wp_error($release)) {
return $result;
}
$package = $this->releasePackage($release);
if ($package === null) {
return $result;
}
return (object) [
'name' => 'Mahak Authenticate Bridge',
'slug' => $this->slug,
'version' => $this->releaseVersion($release),
'author' => 'Dabestaniha',
'homepage' => $release['html_url'] ?? 'https://github.com/dabestaniha/mahak-authenticate-bridge',
'download_link' => $package,
'requires_php' => '7.4',
'sections' => [
'description' => 'Authenticate WordPress users via JSON Web Tokens from the Mahakiha application.',
'changelog' => nl2br(esc_html((string) ($release['body'] ?? ''))),
],
];
}
public function checkNow(): void
{
if (!current_user_can('update_plugins')) {
wp_die(esc_html__('Sorry, you are not allowed to update plugins.'));
}
check_admin_referer(self::CHECK_ACTION);
delete_site_transient(self::RELEASE_CACHE);
delete_site_transient('update_plugins');
wp_update_plugins();
wp_safe_redirect(add_query_arg(
['page' => config('mahak.settings-page'), 'update-checked' => '1'],
admin_url('options-general.php')
));
exit;
}
public static function renderUpdatePanel(): void
{
if (self::$instance === null || !current_user_can('update_plugins')) {
return;
}
$updater = self::$instance;
$release = $updater->latestRelease();
echo '<hr><h2>'.esc_html__('Plugin updates').'</h2>';
echo '<p>'.sprintf(
esc_html__('Installed version: %s'),
'<strong>'.esc_html(MAHAK_AUTHENTICATE_BRIDGE_VERSION).'</strong>'
).'</p>';
if (is_wp_error($release)) {
echo '<div class="notice notice-warning inline"><p>'.esc_html($release->get_error_message()).'</p></div>';
} else {
$update = $updater->availableUpdate($release);
if ($update !== null) {
echo '<p>'.sprintf(esc_html__('Version %s is available.'), esc_html($update->new_version)).'</p>';
$updateUrl = wp_nonce_url(
self_admin_url('update.php?action=upgrade-plugin&plugin='.rawurlencode($updater->plugin)),
'upgrade-plugin_'.$updater->plugin
);
echo '<p><a class="button button-primary" href="'.esc_url($updateUrl).'">'.esc_html__('Update now').'</a></p>';
} else {
echo '<p>'.esc_html__('You are using the latest available version.').'</p>';
}
}
echo '<form method="post" action="'.esc_url(admin_url('admin-post.php')).'">';
echo '<input type="hidden" name="action" value="'.esc_attr(self::CHECK_ACTION).'">';
wp_nonce_field(self::CHECK_ACTION);
submit_button(esc_html__('Check for updates'), 'secondary', 'submit', false);
echo '</form>';
}
private function availableUpdate(?array $release = null): ?object
{
$release = $release ?? $this->latestRelease();
if (is_wp_error($release)) {
return null;
}
$version = $this->releaseVersion($release);
$package = $this->releasePackage($release);
if ($version === '' || $package === null || !version_compare($version, MAHAK_AUTHENTICATE_BRIDGE_VERSION, '>')) {
return null;
}
return (object) [
'id' => $release['html_url'] ?? self::API_URL,
'slug' => $this->slug,
'plugin' => $this->plugin,
'new_version' => $version,
'url' => $release['html_url'] ?? '',
'package' => $package,
'requires_php' => '7.4',
];
}
private function latestRelease()
{
$cached = get_site_transient(self::RELEASE_CACHE);
if (is_array($cached)) {
return $cached;
}
$response = wp_remote_get(self::API_URL, [
'headers' => [
'Accept' => 'application/vnd.github+json',
'User-Agent' => 'Mahak-Authenticate-Bridge/'.MAHAK_AUTHENTICATE_BRIDGE_VERSION,
],
'timeout' => 10,
]);
if (is_wp_error($response)) {
return new \WP_Error('mahak_update_request_failed', 'Could not contact GitHub to check for plugin updates.');
}
if (wp_remote_retrieve_response_code($response) !== 200) {
return new \WP_Error('mahak_update_response_invalid', 'No published plugin release is currently available on GitHub.');
}
$release = json_decode(wp_remote_retrieve_body($response), true);
if (!is_array($release) || !empty($release['draft']) || !empty($release['prerelease'])) {
return new \WP_Error('mahak_update_release_invalid', 'GitHub returned invalid plugin release information.');
}
if ($this->releaseVersion($release) === '' || $this->releasePackage($release) === null) {
return new \WP_Error(
'mahak_update_asset_missing',
'The latest GitHub release does not contain a valid mahak-authenticate-bridge.zip package.'
);
}
set_site_transient(self::RELEASE_CACHE, $release, 6 * HOUR_IN_SECONDS);
return $release;
}
private function releaseVersion(array $release): string
{
return ltrim((string) ($release['tag_name'] ?? ''), 'vV');
}
private function releasePackage(array $release): ?string
{
foreach (($release['assets'] ?? []) as $asset) {
if (($asset['name'] ?? '') !== self::RELEASE_ASSET) {
continue;
}
$url = esc_url_raw((string) ($asset['browser_download_url'] ?? ''));
$path = (string) wp_parse_url($url, PHP_URL_PATH);
if (
$url !== ''
&& wp_parse_url($url, PHP_URL_SCHEME) === 'https'
&& wp_parse_url($url, PHP_URL_HOST) === 'github.com'
&& strpos($path, '/dabestaniha/mahak-authenticate-bridge/releases/download/') === 0
) {
return $url;
}
}
return null;
}
}