Rename the repository to woo-discount-manager, keep the installed slug
Kristian named the five plugins as a family, and three repos disagreed with what
he calls them. His decision: rename the repo, never the installed slug.
MEASURED BEFORE DOING IT, because four published releases carry the old URL in
their Update URI header. A throwaway repo was created, renamed and deleted:
old API path 301 -> new
old web path 301 -> new
old /releases/download/... path 301 -> new
and WordPress follows up to 5 redirects by default (class-wp-http.php:191), on
GET. Confirmed afterwards against the real thing: the old feed URL resolves to
v1.4.5 and the old download path still serves 48185 bytes.
So a shop on 1.4.2 keeps updating through the redirect, and 1.4.6 moves it onto
the new address directly.
PLUGIN_SLUG and PLUGIN_MAIN are deliberately untouched and now disagree with the
repository name. That is permanent: the installed directory has been
woocommerce-email-discount-manager since the first release and is on a live
shop, and changing it would not move that installation — it would put a SECOND
copy beside it and leave the old one active. ci/plugin.conf says so in the file,
because otherwise somebody will helpfully align them.
Also in, both for the family rather than for this plugin alone:
- docs/REUSE.md gains the hostname-keyed hook as a first-class section. It is
the only bug we have found that is CAUSED by there being more than one plugin,
so it is invisible to anyone reading a single repo — along with the same shape
in a shared-*.php file copied into five plugins, which fatals with "Cannot
redeclare" on the second activation without function_exists() guards.
- ci/static.sh fails on anything shaped like a credential in a tracked file.
Suggested by the AI-manager port: a pushed key cannot be unpushed, only
rotated. Deliberately narrow — sk-ant-, sk-, ghp_, gho_, github_pat_, AKIA,
PRIVATE KEY blocks — because a broad entropy heuristic fires on minified
assets and a check that cries wolf gets commented out.
Verified failing before being believed: a file containing sk-ant-api03-... makes
the step report the filename and the suite exit 1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
5e95893c1a
commit
f14c51dcf2
@@ -140,6 +140,65 @@ None of them were caught by reading the code, and a green build is exactly what
|
||||
a broken check looks like. So: break it on purpose once, watch it go red, then
|
||||
put it back. It costs one run.
|
||||
|
||||
## The bug you cannot find by reading your own repo
|
||||
|
||||
🔴 **`update_plugins_{$hostname}` is keyed on the hostname, not the
|
||||
repository.** Every plugin whose `Update URI` points at the same host shares one
|
||||
hook, and WordPress runs **every** callback on it for **every** one of those
|
||||
plugins:
|
||||
|
||||
```php
|
||||
// wp-includes/update.php
|
||||
$update = apply_filters( "update_plugins_{$hostname}", false, $plugin_data, $plugin_file, $locales );
|
||||
```
|
||||
|
||||
Answer unconditionally and you offer your release to your neighbour. Measured on
|
||||
a live shop: a subscriptions plugin was shown this one's 1.4.4 and told it was
|
||||
an update for itself. Load order decides who wins, so which plugin gets
|
||||
corrupted is arbitrary — and pointed at one with auto-updates enabled, it
|
||||
installs a stranger's zip over itself, unattended, on a shop taking money.
|
||||
|
||||
```php
|
||||
add_filter( "update_plugins_{$host}", array( $this, 'offer_update' ), 10, 3 );
|
||||
|
||||
public function offer_update( $update, $plugin_data, $plugin_file = '' ) {
|
||||
if ( '' !== $plugin_file && plugin_basename( SELF_FILE ) !== $plugin_file ) {
|
||||
return $update;
|
||||
}
|
||||
```
|
||||
|
||||
Both halves. Leave the accepted-argument count at 2 and the third argument never
|
||||
arrives, so the guard compares against an empty string and does nothing — the
|
||||
same shape of bug it exists to fix. `plugins_api` is safe if it guards on
|
||||
`$args->slug`.
|
||||
|
||||
**No integration test can find this.** A site test installs one plugin from that
|
||||
host, so the collision cannot arise there. The assertion is the entire defence:
|
||||
|
||||
```php
|
||||
wc_edm_ok( 'another plugin on the same host is not offered our release',
|
||||
false === $updater->offer_update( false, $data, 'someone-else/someone-else.php' ) );
|
||||
```
|
||||
|
||||
The same shape applies to anything else shared between sibling plugins. A
|
||||
`shared-*.php` file copied byte-identically into five plugins fatals with
|
||||
`Cannot redeclare` on the second activation unless everything in it is wrapped
|
||||
in `function_exists()` / `class_exists()` / `defined()` — and then the copy that
|
||||
loads first wins, which is alphabetical order, which is arbitrary. Freeze the
|
||||
contract rather than versioning the file.
|
||||
|
||||
## The repository name and the installed slug are different things
|
||||
|
||||
The repo may be renamed freely — Gitea keeps a 301 on both the API and the deep
|
||||
release-download paths (measured), and WordPress follows up to 5 redirects by
|
||||
default, so installed copies keep updating through the old `Update URI`.
|
||||
|
||||
The **installed** slug can never change. It is `<slug>/<main file>` in
|
||||
`wp-content/plugins`, and a new one installs a second copy beside the first
|
||||
while the old one stays active. So `PLUGIN_SLUG` in `ci/plugin.conf` is allowed
|
||||
to disagree with the repository name forever, and if it does, say so in the file
|
||||
or somebody will helpfully align them.
|
||||
|
||||
## Two guards worth understanding before you move them
|
||||
|
||||
**`.gitea/workflows/` hides `.github/workflows/` completely.** Gitea uses the
|
||||
|
||||
Reference in New Issue
Block a user