CI / Tests (PHP 8.1) (pull_request) Successful in 47s
CI / Tests (PHP 8.2) (pull_request) Successful in 47s
CI / No Debug Code (pull_request) Successful in 3s
CI / Coding Standards (pull_request) Successful in 3m7s
CI / Tests (PHP 8.3) (pull_request) Successful in 2m38s
CI / PHPStan (pull_request) Successful in 2m50s
CI / Build Plugin Zip (pull_request) Skipped
WordPress only renders the "Enable auto-updates" toggle for a plugin that appears in the update_plugins transient's response or no_update list, which is what sets core's update-supported flag. UpdateChecker only populated the response side (when a newer release existed), so between releases the plugin was absent from the transient and the toggle never showed. provideUpdate() now returns a no_update payload (installed version, empty package) whenever no newer release is offered — including when the release lookup fails — so the plugin stays in the transient and the toggle appears. The response path (one-click and unattended updates) is unchanged. Bumps to 1.1.1 so the fix ships to installed sites via the self-updater. Co-Authored-By: Claude Opus 4.8 <[email protected]>
81 lines
3.7 KiB
Markdown
81 lines
3.7 KiB
Markdown
# Feature: Plugin Self-Update from Gitea Releases
|
|
|
|
## Overview
|
|
WordPress sites running this plugin receive updates directly from the Gitea
|
|
repository's releases — no wordpress.org listing and no manual zip uploads.
|
|
Publishing a release is the whole deploy: bump the version, merge to `main`,
|
|
tag `vX.Y.Z` in Gitea. Every site sees the update on its next check and can
|
|
install it with one click, or unattended if the site admin enables
|
|
auto-updates for the plugin.
|
|
|
|
## How It Works
|
|
|
|
### Release side (`.gitea/workflows/release.yml`)
|
|
Pushing a `v*` tag (including tags created through Gitea's "New Release" UI)
|
|
triggers the release workflow, which:
|
|
|
|
1. Fails if the tag does not match the `Version:` plugin header — a mismatch
|
|
would make sites see a phantom update forever, or never see a real one.
|
|
2. Runs the test suite.
|
|
3. Builds the distributable zip via `composer build` (`bin/build-zip.sh`):
|
|
a single top-level `unsupervised-schedular/` folder with a production
|
|
(no-dev) Composer autoloader.
|
|
4. Creates the release for the tag (or reuses one created via the UI) and
|
|
attaches the zip as a release asset. Versions containing a hyphen
|
|
(e.g. `1.2.3-rc.1`) are flagged as pre-releases.
|
|
|
|
The attached asset — not Gitea's auto-generated source archive — is the
|
|
update package. Source archives have the wrong top-level folder name and no
|
|
`vendor/` directory, so WordPress could not install them.
|
|
|
|
### Site side (`src/Update/UpdateChecker.php`)
|
|
The plugin header declares:
|
|
|
|
```
|
|
Update URI: https://git.unsupervised.ca/Unsupervised/unsupervised-scheduler
|
|
```
|
|
|
|
Since WP 5.8 that header both blocks wordpress.org from ever serving an
|
|
update for a same-slug plugin and makes core fire the
|
|
`update_plugins_git.unsupervised.ca` filter during update checks.
|
|
`UpdateChecker` (registered in `Plugin::boot()`) answers that filter:
|
|
|
|
1. Fetches `GET /api/v1/repos/Unsupervised/unsupervised-scheduler/releases/latest`
|
|
(anonymous — the repo is public). The `/latest` endpoint excludes drafts
|
|
and pre-releases, so `-rc` builds are never offered to sites.
|
|
2. Caches the result (including failures) in the
|
|
`us_schedular_latest_release` transient for 6 hours.
|
|
3. Strips the leading `v` from the tag and compares against `USC_VERSION`
|
|
with `version_compare`; PHP orders `1.0.0-rc.2 < 1.0.0` correctly.
|
|
4. When newer, returns the release's first `.zip` asset as the update
|
|
package. Core takes over from there: Plugins-screen notice, one-click
|
|
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
|
|
"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
|
|
1. Bump the version in `unsupervised-schedular.php` (both the `Version:`
|
|
header and the `USC_VERSION` constant) and merge to `main`.
|
|
2. Tag the merge commit `vX.Y.Z` — via Gitea's New Release UI or
|
|
`git tag vX.Y.Z && git push origin vX.Y.Z`.
|
|
3. The release workflow attaches the zip; sites pick the update up on their
|
|
next check (twice daily via cron, or immediately from
|
|
Dashboard → Updates → Check again).
|
|
|
|
## Classes
|
|
|
|
| Class | Responsibility |
|
|
|---|---|
|
|
| `Update\UpdateChecker` | Answers core's `update_plugins_{hostname}` filter from the Gitea releases API |
|
|
|
|
## Tests
|
|
- `tests/Unit/Update/UpdateCheckerTest.php`
|