Merge pull request 'Show the Enable auto-updates toggle for the self-updater' (#101) from feature/auto-update-toggle into main
CI / Coding Standards (push) Successful in 2m52s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.2) (push) Successful in 40s
CI / Tests (PHP 8.1) (push) Successful in 54s
CI / PHPStan (push) Successful in 2m49s
CI / Tests (PHP 8.3) (push) Successful in 2m40s
Release / Build and Publish Release (push) Successful in 3m1s
Release / Open next-version bump PR (push) Successful in 5s
CI / Build Plugin Zip (push) Successful in 2m47s
CI / Coding Standards (push) Successful in 2m52s
CI / No Debug Code (push) Successful in 2s
CI / Tests (PHP 8.2) (push) Successful in 40s
CI / Tests (PHP 8.1) (push) Successful in 54s
CI / PHPStan (push) Successful in 2m49s
CI / Tests (PHP 8.3) (push) Successful in 2m40s
Release / Build and Publish Release (push) Successful in 3m1s
Release / Open next-version bump PR (push) Successful in 5s
CI / Build Plugin Zip (push) Successful in 2m47s
Reviewed-on: #101
This commit was merged in pull request #101.
This commit is contained in:
@@ -13,6 +13,9 @@ each change under the current top section as you work.
|
|||||||
|
|
||||||
## [1.1.1]
|
## [1.1.1]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- The **Enable auto-updates** toggle now appears for the plugin on the Plugins screen. The self-updater now reports the plugin to WordPress even when it is already current, so core marks it update-supported and shows the toggle; previously the toggle was hidden between releases.
|
||||||
|
|
||||||
## [1.1.0]
|
## [1.1.0]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -50,9 +50,16 @@ update for a same-slug plugin and makes core fire the
|
|||||||
4. When newer, returns the release's first `.zip` asset as the update
|
4. When newer, returns the release's first `.zip` asset as the update
|
||||||
package. Core takes over from there: Plugins-screen notice, one-click
|
package. Core takes over from there: Plugins-screen notice, one-click
|
||||||
update, and WP-Cron auto-updates if enabled.
|
update, and WP-Cron auto-updates if enabled.
|
||||||
|
5. When not newer — the site is current, or the lookup failed — returns a
|
||||||
|
`no_update` payload (installed version, empty package). This keeps the
|
||||||
|
plugin in core's `update_plugins` transient so core's `update-supported`
|
||||||
|
flag stays set and the **Enable auto-updates** toggle shows on the
|
||||||
|
Plugins screen. Without it, an off-directory plugin is absent from the
|
||||||
|
transient between releases and the toggle never appears.
|
||||||
|
|
||||||
Any API failure, malformed response, or asset-less release degrades to
|
Any API failure, malformed response, or asset-less release degrades to
|
||||||
"no update available" — never an error surfaced to the site.
|
"no update available" (the `no_update` payload) — never an error surfaced
|
||||||
|
to the site, and never a lost auto-update toggle during a Gitea blip.
|
||||||
|
|
||||||
## Cutting a Release
|
## Cutting a Release
|
||||||
1. Bump the version in `unsupervised-schedular.php` (both the `Version:`
|
1. Bump the version in `unsupervised-schedular.php` (both the `Version:`
|
||||||
|
|||||||
@@ -75,9 +75,18 @@ class UpdateChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `update_plugins_{hostname}` filter callback. Returns the incoming
|
* `update_plugins_{hostname}` filter callback.
|
||||||
* value untouched unless a newer release with a zip asset exists, in
|
*
|
||||||
* which case it returns the update array core expects.
|
* For a newer release with a zip asset, returns the update array core
|
||||||
|
* files under the transient's `response` list (the update offer).
|
||||||
|
* Otherwise — the plugin is current, or the release lookup failed — it
|
||||||
|
* returns a payload with the installed version and no package, which core
|
||||||
|
* files under `no_update`. That `no_update` entry is what sets core's
|
||||||
|
* `update-supported` flag and makes the "Enable auto-updates" toggle
|
||||||
|
* appear on the Plugins screen; without it, an off-directory plugin is
|
||||||
|
* absent from the transient between releases and the toggle never shows.
|
||||||
|
*
|
||||||
|
* The incoming value is only passed through untouched for other plugins.
|
||||||
*/
|
*/
|
||||||
public function provideUpdate( mixed $update, mixed $plugin_data, mixed $plugin_file ): mixed {
|
public function provideUpdate( mixed $update, mixed $plugin_data, mixed $plugin_file ): mixed {
|
||||||
if ( plugin_basename( USC_PLUGIN_FILE ) !== $plugin_file ) {
|
if ( plugin_basename( USC_PLUGIN_FILE ) !== $plugin_file ) {
|
||||||
@@ -86,14 +95,8 @@ class UpdateChecker {
|
|||||||
|
|
||||||
$release = $this->latestRelease();
|
$release = $this->latestRelease();
|
||||||
|
|
||||||
if ( '' === $release['version'] || '' === $release['package'] ) {
|
if ( '' !== $release['version'] && '' !== $release['package']
|
||||||
return $update;
|
&& version_compare( $release['version'], USC_VERSION, '>' ) ) {
|
||||||
}
|
|
||||||
|
|
||||||
if ( version_compare( $release['version'], USC_VERSION, '<=' ) ) {
|
|
||||||
return $update;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'slug' => 'unsupervised-schedular',
|
'slug' => 'unsupervised-schedular',
|
||||||
'version' => $release['version'],
|
'version' => $release['version'],
|
||||||
@@ -102,6 +105,17 @@ class UpdateChecker {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No newer release: answer with a `no_update` payload so core keeps
|
||||||
|
// the plugin in the update transient and shows the auto-update toggle.
|
||||||
|
// The empty package leaves core nothing to auto-install, as intended.
|
||||||
|
return [
|
||||||
|
'slug' => 'unsupervised-schedular',
|
||||||
|
'version' => USC_VERSION,
|
||||||
|
'url' => self::REPO_URL,
|
||||||
|
'package' => '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The latest published release, from the transient cache when fresh.
|
* The latest published release, from the transient cache when fresh.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -32,6 +32,22 @@ class UpdateCheckerTest extends TestCase
|
|||||||
return ['name' => $name, 'browser_download_url' => self::PACKAGE_URL];
|
return ['name' => $name, 'browser_download_url' => self::PACKAGE_URL];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The payload provideUpdate() returns when no newer release is offered.
|
||||||
|
* Core files this under the transient's `no_update` list, which is what
|
||||||
|
* makes the "Enable auto-updates" toggle appear. USC_VERSION is 1.0.0 in
|
||||||
|
* the test bootstrap.
|
||||||
|
*/
|
||||||
|
private function noUpdatePayload(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'slug' => 'unsupervised-schedular',
|
||||||
|
'version' => '1.0.0',
|
||||||
|
'url' => UpdateChecker::REPO_URL,
|
||||||
|
'package' => '',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function testRegisterHooksHostnameFilter(): void
|
public function testRegisterHooksHostnameFilter(): void
|
||||||
{
|
{
|
||||||
Filters\expectAdded('update_plugins_git.unsupervised.ca')->once();
|
Filters\expectAdded('update_plugins_git.unsupervised.ca')->once();
|
||||||
@@ -109,7 +125,7 @@ class UpdateCheckerTest extends TestCase
|
|||||||
self::assertFalse($result);
|
self::assertFalse($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNoUpdateWhenReleaseIsNotNewer(): void
|
public function testNoUpdatePayloadWhenReleaseIsNotNewer(): void
|
||||||
{
|
{
|
||||||
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
||||||
Functions\when('get_transient')->justReturn(false);
|
Functions\when('get_transient')->justReturn(false);
|
||||||
@@ -118,7 +134,9 @@ class UpdateCheckerTest extends TestCase
|
|||||||
|
|
||||||
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
||||||
|
|
||||||
self::assertFalse($result);
|
// Current version → core files this under `no_update` so the
|
||||||
|
// auto-update toggle stays visible; no package to install.
|
||||||
|
self::assertSame($this->noUpdatePayload(), $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUsesCachedReleaseWithoutHittingApi(): void
|
public function testUsesCachedReleaseWithoutHittingApi(): void
|
||||||
@@ -134,7 +152,7 @@ class UpdateCheckerTest extends TestCase
|
|||||||
self::assertSame('2.0.0', $result['version']);
|
self::assertSame('2.0.0', $result['version']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testApiFailureIsCachedAndReturnsUpdateUnchanged(): void
|
public function testApiFailureIsCachedAndStillReportsUpdateSupport(): void
|
||||||
{
|
{
|
||||||
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
||||||
Functions\when('get_transient')->justReturn(false);
|
Functions\when('get_transient')->justReturn(false);
|
||||||
@@ -150,10 +168,12 @@ class UpdateCheckerTest extends TestCase
|
|||||||
|
|
||||||
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
||||||
|
|
||||||
self::assertFalse($result);
|
// Even with the lookup failed we still return the `no_update` payload,
|
||||||
|
// so the auto-update toggle does not flicker away during a Gitea blip.
|
||||||
|
self::assertSame($this->noUpdatePayload(), $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNon200ResponseReturnsUpdateUnchanged(): void
|
public function testNon200ResponseReturnsNoUpdatePayload(): void
|
||||||
{
|
{
|
||||||
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
||||||
Functions\when('get_transient')->justReturn(false);
|
Functions\when('get_transient')->justReturn(false);
|
||||||
@@ -162,7 +182,7 @@ class UpdateCheckerTest extends TestCase
|
|||||||
|
|
||||||
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
||||||
|
|
||||||
self::assertFalse($result);
|
self::assertSame($this->noUpdatePayload(), $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPicksFirstZipAssetAndSkipsOthers(): void
|
public function testPicksFirstZipAssetAndSkipsOthers(): void
|
||||||
@@ -181,7 +201,7 @@ class UpdateCheckerTest extends TestCase
|
|||||||
self::assertSame(self::PACKAGE_URL, $result['package']);
|
self::assertSame(self::PACKAGE_URL, $result['package']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testReleaseWithoutZipAssetOffersNoUpdate(): void
|
public function testReleaseWithoutZipAssetOffersNoUpdatePayload(): void
|
||||||
{
|
{
|
||||||
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
||||||
Functions\when('get_transient')->justReturn(false);
|
Functions\when('get_transient')->justReturn(false);
|
||||||
@@ -192,10 +212,12 @@ class UpdateCheckerTest extends TestCase
|
|||||||
|
|
||||||
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
||||||
|
|
||||||
self::assertFalse($result);
|
// No installable package means no update to offer, but we still keep
|
||||||
|
// the plugin in `no_update` so the toggle shows.
|
||||||
|
self::assertSame($this->noUpdatePayload(), $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMalformedApiBodyOffersNoUpdate(): void
|
public function testMalformedApiBodyOffersNoUpdatePayload(): void
|
||||||
{
|
{
|
||||||
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
Functions\when('plugin_basename')->justReturn(self::PLUGIN_FILE);
|
||||||
Functions\when('get_transient')->justReturn(false);
|
Functions\when('get_transient')->justReturn(false);
|
||||||
@@ -207,6 +229,6 @@ class UpdateCheckerTest extends TestCase
|
|||||||
|
|
||||||
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
$result = (new UpdateChecker())->provideUpdate(false, [], self::PLUGIN_FILE);
|
||||||
|
|
||||||
self::assertFalse($result);
|
self::assertSame($this->noUpdatePayload(), $result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user