Skip to content

Commit bba2968

Browse files
committed
Merge branch 'develop'
2 parents f1386a7 + 239cc02 commit bba2968

File tree

7 files changed

+132
-25
lines changed

7 files changed

+132
-25
lines changed

CHANGES.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
#### [unreleased]
22

3+
#### 12.22.0 / 2026-01-13
4+
* added `gu_dev_release_asset` filter for dev release assets
5+
* added `channel` query arg for dev release assets when using `update-api` REST endpoint
6+
* send a saved access token with `update-api` REST API if one exists
7+
* omit non-shared packages from REST API
8+
39
#### 12.21.0 / 2025-12-31 🎆
410
* remove `git_updater_plugin_updates` and `git_updater_theme_updates` options, see [#1119](https://github.com/afragen/git-updater/issues/1119)
511
* add `gu_plugin_name()` to return plugin name, slug or slug-didhash

git-updater.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* Plugin ID: did:plc:afjf7gsjzsqmgc7dlhb553mv
1414
* Plugin URI: https://git-updater.com
1515
* Description: A plugin to automatically update GitHub hosted plugins, themes, and language packs. Additional API plugins available for Bitbucket, GitLab, Gitea, and Gist.
16-
* Version: 12.21.0
16+
* Version: 12.22.0
1717
* Author: Andy Fragen
1818
* Author URI: https://thefragens.com
1919
* Security: [email protected]

languages/git-updater.pot

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
# Copyright (C) 2025 Andy Fragen
1+
# Copyright (C) 2026 Andy Fragen
22
# This file is distributed under the GPL-3.0-or-later.
33
msgid ""
44
msgstr ""
5-
"Project-Id-Version: Git Updater 12.20.2.3\n"
5+
"Project-Id-Version: Git Updater 12.21.0.1\n"
66
"Report-Msgid-Bugs-To: https://github.com/afragen/git-updater/issues\n"
77
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
88
"Language-Team: LANGUAGE <[email protected]>\n"
99
"MIME-Version: 1.0\n"
1010
"Content-Type: text/plain; charset=UTF-8\n"
1111
"Content-Transfer-Encoding: 8bit\n"
12-
"POT-Creation-Date: 2025-12-31T19:57:25+00:00\n"
12+
"POT-Creation-Date: 2026-01-01T06:49:46+00:00\n"
1313
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1414
"X-Generator: WP-CLI 2.12.0\n"
1515
"X-Domain: git-updater\n"
@@ -128,35 +128,35 @@ msgstr ""
128128
msgid "Add-Ons"
129129
msgstr ""
130130

131-
#: src/Git_Updater/API/GitHub_API.php:471
131+
#: src/Git_Updater/API/GitHub_API.php:485
132132
msgid "GitHub Personal Access Token"
133133
msgstr ""
134134

135-
#: src/Git_Updater/API/GitHub_API.php:478
135+
#: src/Git_Updater/API/GitHub_API.php:492
136136
msgid "GitHub.com Access Token"
137137
msgstr ""
138138

139-
#: src/Git_Updater/API/GitHub_API.php:494
139+
#: src/Git_Updater/API/GitHub_API.php:508
140140
msgid "GitHub Private Settings"
141141
msgstr ""
142142

143-
#: src/Git_Updater/API/GitHub_API.php:521
143+
#: src/Git_Updater/API/GitHub_API.php:535
144144
msgid "Enter your GitHub Access Token. Leave empty for public repositories."
145145
msgstr ""
146146

147-
#: src/Git_Updater/API/GitHub_API.php:528
147+
#: src/Git_Updater/API/GitHub_API.php:542
148148
msgid "Enter your personal GitHub.com or GitHub Enterprise Access Token to avoid API access limits."
149149
msgstr ""
150150

151-
#: src/Git_Updater/API/GitHub_API.php:541
151+
#: src/Git_Updater/API/GitHub_API.php:555
152152
msgid "GitHub Access Token"
153153
msgstr ""
154154

155-
#: src/Git_Updater/API/GitHub_API.php:555
155+
#: src/Git_Updater/API/GitHub_API.php:569
156156
msgid "GitHub"
157157
msgstr ""
158158

159-
#: src/Git_Updater/API/GitHub_API.php:569
159+
#: src/Git_Updater/API/GitHub_API.php:583
160160
msgid "Enter GitHub Access Token for private GitHub repositories."
161161
msgstr ""
162162

src/Git_Updater/API/GitHub_API.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,20 @@ public function construct_download_link( $branch_switch = false ) {
161161
$release_assets['assets'] = $release_assets['assets'] ?? [];
162162
$release_asset = reset( $release_assets['assets'] );
163163

164+
/*
165+
* Check if dev release asset is newer than latest release asset.
166+
*
167+
* @param bool
168+
* @param $this->type Repo type object.
169+
*/
170+
if ( apply_filters( 'gu_dev_release_asset', false, $this->type ) ) {
171+
$current_asset_version = array_key_first( $release_assets['assets'] ) ?? '';
172+
$current_dev_asset_version = array_key_first( $release_assets['dev_assets'] ) ?? '';
173+
if ( version_compare( $current_asset_version, $current_dev_asset_version, '<' ) ) {
174+
$release_asset = reset( $release_assets['dev_assets'] );
175+
}
176+
}
177+
164178
if ( empty( $this->response['release_asset_download'] ) ) {
165179
$this->set_repo_cache( 'release_asset_download', $release_asset );
166180
}

src/Git_Updater/REST/REST_API.php

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -446,10 +446,23 @@ public function get_api_data( WP_REST_Request $request ) {
446446
if ( ! $slug ) {
447447
return (object) [ 'error' => 'The REST request likely has an invalid query argument. It requires a `slug`.' ];
448448
}
449+
$channel = null !== $request->get_param( 'channel' );
449450
$gu_plugins = Singleton::get_instance( 'Fragen\Git_Updater\Plugin', $this )->get_plugin_configs();
450451
$gu_themes = Singleton::get_instance( 'Fragen\Git_Updater\Theme', $this )->get_theme_configs();
451452
$gu_repos = array_merge( $gu_plugins, $gu_themes );
452453

454+
// Don't allow non-shared repos via this API. Set via Additions tab.
455+
$additions = get_site_option( 'git_updater_additions', [] );
456+
foreach ( $additions as $addition ) {
457+
$addition_slug = str_contains( $addition['type'], 'plugin' ) ? dirname( $addition['slug'] ) : $addition['slug'];
458+
459+
if ( $addition_slug === $slug ) {
460+
if ( isset( $addition['private_package'] ) && true === (bool) $addition['private_package'] ) {
461+
return (object) [ 'error' => 'Specified repo is not shared.' ];
462+
}
463+
}
464+
}
465+
453466
if ( ! array_key_exists( $slug, $gu_repos ) ) {
454467
return (object) [ 'error' => 'Specified repo does not exist.' ];
455468
}
@@ -458,14 +471,36 @@ public function get_api_data( WP_REST_Request $request ) {
458471
$repo_data = Singleton::get_instance( 'Fragen\Git_Updater\Base', $this )->get_remote_repo_meta( $gu_repos[ $slug ] );
459472

460473
if ( ! is_object( $repo_data ) || '0.0.0' === $repo_data->remote_version ) {
461-
return (object) [ 'error' => 'API data response is incorrect.' ];
474+
$rate_limit = 'github' === $repo_data->git ? $this->get_github_rate_limit_headers() : [];
475+
return (object) [
476+
'error' => 'API data response is incorrect.',
477+
'rate_limit' => $rate_limit,
478+
];
479+
}
480+
481+
// Get release assets and dev release assets.
482+
$release_assets = $repo_data->release_assets ?? [];
483+
$dev_release_assets = $repo_data->dev_release_assets ?? [];
484+
485+
// Is dev channel more current than stable?
486+
$current_asset_version = array_key_first( $release_assets ) ?? '';
487+
$current_dev_asset_version = array_key_first( $dev_release_assets ) ?? '';
488+
$use_channel = version_compare( $current_asset_version, $current_dev_asset_version, '<' );
489+
490+
// Set remote version based on channel selection.
491+
$remote_version = $repo_data->remote_version;
492+
if ( $repo_data->release_asset && $channel && $use_channel ) {
493+
$remote_version = $current_dev_asset_version;
494+
$remote_version = ltrim( $remote_version, 'v' );
462495
}
463496

464497
$last_updated = ! empty( $repo_data->created_at ) ? reset( $repo_data->created_at ) : $repo_data->last_updated;
465498

499+
$last_updated = $channel && $use_channel && ! empty( $repo_data->dev_created_at ) ? reset( $repo_data->dev_created_at ) : $last_updated;
500+
466501
// Get versions from release assets or tags. Limit to 20.
467502
if ( $repo_data->release_asset ) {
468-
$versions = $repo_data->release_assets ?? [];
503+
$versions = $channel && $use_channel ? $dev_release_assets : $release_assets;
469504
} else {
470505
$versions = $repo_data->tags ?? [];
471506
}
@@ -482,8 +517,10 @@ public function get_api_data( WP_REST_Request $request ) {
482517
'update_uri' => $repo_data->update_uri ?? '',
483518
'is_private' => $repo_data->is_private,
484519
'dot_org' => $repo_data->dot_org,
520+
'dev_channel' => $channel,
521+
'use_dev_channel' => $channel && $use_channel,
485522
'release_asset' => $repo_data->release_asset,
486-
'version' => $repo_data->remote_version,
523+
'version' => $remote_version,
487524
'author' => $repo_data->author,
488525
'author_uri' => $repo_data->author_uri ?? '',
489526
'security' => $repo_data->security ?? '',
@@ -501,7 +538,7 @@ public function get_api_data( WP_REST_Request $request ) {
501538
'download_link' => $repo_data->download_link ?? '',
502539
'tags' => $repo_data->readme_tags ?? [],
503540
'versions' => $versions,
504-
'created_at' => $repo_data->created_at,
541+
'created_at' => $channel && $use_channel ? $repo_data->dev_created_at : $repo_data->created_at,
505542
'donate_link' => $repo_data->donate_link,
506543
'banners' => $repo_data->banners,
507544
'icons' => $repo_data->icons,
@@ -541,9 +578,8 @@ public function get_api_data( WP_REST_Request $request ) {
541578
}
542579
}
543580

544-
if ( ! $repo_api_data['is_private']
545-
&& ! in_array( $repo_api_data['git'], [ 'gitlab', 'gitea' ], true )
546-
) {
581+
$private_or_token = $repo_api_data['is_private'] || ! empty( $this->get_class_vars( 'API\API', 'options' )[ $slug ] );
582+
if ( ! $private_or_token && ! in_array( $repo_api_data['git'], [ 'gitlab', 'gitea' ], true ) ) {
547583
unset( $repo_api_data['auth_header']['headers']['Authorization'] );
548584
}
549585

src/Git_Updater/Traits/API_Common.php

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,10 @@ private function parse_release_asset( $git, $request, $response ) {
6666
$response = [];
6767
$response[] = $release ?? [];
6868
}
69-
$release_assets = [];
70-
$created_at = [];
69+
$release_assets = [];
70+
$created_at = [];
71+
$dev_release_assets = [];
72+
$dev_created_at = [];
7173
foreach ( $response as $release ) {
7274
// Ignore leading 'v' and skip anything with dash or words.
7375
if ( ! preg_match( '/[^v]+[-a-z]+/', $release->tag_name ) ) {
@@ -79,12 +81,26 @@ private function parse_release_asset( $git, $request, $response ) {
7981
}
8082
}
8183
}
84+
// Dev releases.
85+
if ( preg_match( '/[^v]+(?:nightly|alpha|beta|RC){1}[0-9]{0,}/', $release->tag_name ) ) {
86+
foreach ( $release->assets as $asset ) {
87+
if ( str_starts_with( $asset->name, $this->type->slug ) ) {
88+
$dev_release_assets[ $release->tag_name ] = $asset->url;
89+
$dev_created_at[ $release->tag_name ] = $asset->created_at;
90+
continue 2;
91+
}
92+
}
93+
}
8294
}
8395
uksort( $release_assets, fn ( $a, $b ) => version_compare( ltrim( $b, 'v' ), ltrim( $a, 'v' ) ) );
8496
uksort( $created_at, fn ( $a, $b ) => version_compare( ltrim( $b, 'v' ), ltrim( $a, 'v' ) ) );
97+
uksort( $dev_release_assets, fn ( $a, $b ) => version_compare( ltrim( $b, 'v' ), ltrim( $a, 'v' ) ) );
98+
uksort( $dev_created_at, fn ( $a, $b ) => version_compare( ltrim( $b, 'v' ), ltrim( $a, 'v' ) ) );
8599
$response = [
86-
'assets' => $release_assets,
87-
'created_at' => $created_at,
100+
'assets' => $release_assets,
101+
'created_at' => $created_at,
102+
'dev_assets' => $dev_release_assets,
103+
'dev_created_at' => $dev_created_at,
88104
];
89105
}
90106

@@ -483,8 +499,10 @@ final public function get_api_release_assets( $git, $request ) {
483499
return false;
484500
}
485501

486-
$this->type->release_assets = $response['assets'] ?? $response;
487-
$this->type->created_at = $response['created_at'] ?? [];
502+
$this->type->release_assets = $response['assets'] ?? $response;
503+
$this->type->created_at = $response['created_at'] ?? [];
504+
$this->type->dev_release_assets = $response['dev_assets'] ?? [];
505+
$this->type->dev_created_at = $response['dev_created_at'] ?? [];
488506

489507
return $response;
490508
}

src/Git_Updater/Traits/GU_Trait.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,11 @@ final public function get_error_codes() {
192192
* @return bool
193193
*/
194194
final public function can_update_repo( $type ) {
195+
if ( isset( $type->dev_release_assets ) && apply_filters( 'gu_dev_release_asset', false, $type ) ) {
196+
$release_asset_version = array_key_first( $type->dev_release_assets ) ?? '';
197+
$release_asset_version = ltrim( $release_asset_version, 'v' );
198+
$type->remote_version = $release_asset_version ?: $type->remote_version;
199+
}
195200
$wp_version_ok = ! empty( $type->requires )
196201
? is_wp_version_compatible( $type->requires )
197202
: true;
@@ -832,4 +837,32 @@ final public function get_file_without_did_hash( $did, $plugin ): string {
832837

833838
return $slug . '/' . $file;
834839
}
840+
841+
/**
842+
* Get GitHub API rate limit headers.
843+
*
844+
* Display ratelimit reset time in minutes.
845+
*
846+
* @return array|WP_Error
847+
*/
848+
final public function get_github_rate_limit_headers() {
849+
$auth_header = Singleton::get_instance( 'Fragen\Git_Updater\API\API', $this )->add_auth_header( [], 'https://api.github.com/rate_limit' );
850+
$response = wp_remote_head( 'https://api.github.com/rate_limit', $auth_header );
851+
852+
if ( is_wp_error( $response ) ) {
853+
return $response;
854+
}
855+
856+
$headers = wp_remote_retrieve_headers( $response );
857+
$data = $headers->getAll();
858+
if ( isset( $data['x-ratelimit-reset'] ) ) {
859+
$reset = (int) $data['x-ratelimit-reset'];
860+
// phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
861+
$data['x-ratelimit-reset'] = date( 'i', $reset - time() ) . ' minutes';
862+
} else {
863+
$data['x-ratelimit-reset'] = '60 minutes';
864+
}
865+
866+
return $data;
867+
}
835868
}

0 commit comments

Comments
 (0)