From c8648d5263f5709e32ea3172cc5bc26e5205737e Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Wed, 4 Nov 2026 14:50:43 +0100 Subject: [PATCH] fix(updater): Paket-Adresse aus Tag und Dateiname bauen, Version 1.0.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gitea meldet browser_download_url hinter dem TLS-Proxy mit http://, der Updater lehnte die Adresse ab und fand kein Update. Die Adresse wird jetzt aus dem geprüften Tag und dem Dateinamen gebaut; release-check.sh prüft genauso. Co-Authored-By: Claude Sonnet 5.5 --- bin/release-check.sh | 6 +++-- readme.txt | 9 +++++--- src/Plugin.php | 2 +- src/Updater/GiteaUpdater.php | 21 ++++++++---------- tests/Unit/Updater/GiteaUpdaterTest.php | 29 ++++++++++++++++++++----- wp-lit-mcp.php | 2 +- 6 files changed, 44 insertions(+), 25 deletions(-) diff --git a/bin/release-check.sh b/bin/release-check.sh index ade91ba..b982aec 100755 --- a/bin/release-check.sh +++ b/bin/release-check.sh @@ -25,8 +25,10 @@ latest="$(curl -fsS "$gitea/api/v1/repos/$repo/releases/latest")" \ tag="$(jq -r .tag_name <<<"$latest")" [[ "$tag" == "v$version" ]] || fail "releases/latest liefert '$tag', erwartet v$version" -zip_url="$(jq -r --arg n "$name" '.assets[] | select(.name == $n) | .browser_download_url' <<<"$latest")" -[[ "$zip_url" == "$gitea/$repo/releases/download/"* ]] || fail "Asset-URL fehlt oder liegt woanders: '$zip_url'" +# The address is built like GiteaUpdater builds it: Gitea behind a TLS proxy reports http:// in browser_download_url. +[[ "$(jq -r --arg n "$name" '[.assets[] | select(.name == $n)] | length' <<<"$latest")" == "1" ]] \ + || fail "Das Release hat kein Asset $name" +zip_url="$gitea/$repo/releases/download/$tag/$name" work="$(mktemp -d)" trap 'rm -rf "$work"' EXIT diff --git a/readme.txt b/readme.txt index 54b77a4..c3939f9 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: mcp, ai, divi, pods, oauth Requires at least: 6.9 Tested up to: 7.1 Requires PHP: 8.1 -Stable tag: 1.0.1 +Stable tag: 1.0.2 License: GPL-2.0-or-later License URI: https://www.gnu.org/licenses/gpl-2.0.html @@ -200,8 +200,8 @@ Bibliothekseintrags. Neue Versionen erscheinen als Release auf git.loosen-it.de; WordPress zeigt sie unter Plugins und Dashboard → Aktualisierungen an wie jedes andere Plugin. Automatisch einspielen lässt sich ein Update mit dem Schalter „Automatische Updates aktivieren“ in der Plugin-Liste. Ein Update ersetzt den Plugin-Ordner; ein Entwicklungs- -Checkout mit .git-Ordner bekommt deshalb kein Update angeboten. Versionen vor 1.0.1 kennen diesen -Weg nicht: der Wechsel auf 1.0.1 ist einmalig von Hand (ZIP hochladen), danach läuft es über WordPress. +Checkout mit .git-Ordner bekommt deshalb kein Update angeboten. Versionen vor 1.0.2 kennen diesen +Weg nicht: der Wechsel auf 1.0.2 ist einmalig von Hand (ZIP hochladen), danach läuft es über WordPress. == Divi-Freigabe == @@ -209,6 +209,9 @@ LIT MCP schreibt nur mit Divi-Versionen, die für diese Plugin-Version geprüft == Changelog == += 1.0.2 = +* Behebt: 1.0.1 fand keine Updates, weil Gitea die Download-Adresse mit http:// meldet. Die Adresse des Pakets wird jetzt selbst aus Tag und Dateiname gebaut. Der Wechsel auf 1.0.2 ist einmalig von Hand. + = 1.0.1 = * Updates über WordPress: Das neueste Release von git.loosen-it.de erscheint als Plugin-Update (Update URI); automatisches Einspielen über den Schalter in der Plugin-Liste. Der Wechsel auf 1.0.1 ist einmalig von Hand. (Version 1.0.0 wurde nie ausgeliefert.) diff --git a/src/Plugin.php b/src/Plugin.php index a919ba8..6de64a7 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -46,7 +46,7 @@ defined('ABSPATH') || exit; */ final class Plugin { - public const VERSION = '1.0.1'; + public const VERSION = '1.0.2'; private static string $pluginFile = ''; private static ?SettingsPage $settingsPage = null; diff --git a/src/Updater/GiteaUpdater.php b/src/Updater/GiteaUpdater.php index 4429636..61b0649 100644 --- a/src/Updater/GiteaUpdater.php +++ b/src/Updater/GiteaUpdater.php @@ -13,7 +13,7 @@ defined('ABSPATH') || exit; * Gitea host, so WordPress asks the filter update_plugins_ (about every 12 hours, which is also the only * caching needed). The release carries the ZIP as asset wp-lit-mcp-.zip. * - * shortcut: no signature – a package is trusted as far as the repository's release downloads and TLS are, so whoever + * shortcut: no signature – the package is trusted as far as the repository's release downloads and TLS are, so whoever * can publish a release there ships code to every site that updates. Public release assets only (WordPress downloads * without credentials). Add a signature checked before unpacking (key outside Gitea) before the plugin runs on sites * you do not control. @@ -90,7 +90,7 @@ final class GiteaUpdater } $version = $match[1]; - $package = self::packageUrl((array) ($body['assets'] ?? []), $version); + $package = self::packageUrl((array) ($body['assets'] ?? []), $tag, $version); if ($package === null) { return self::failed( sprintf('release %s has no asset %s-%s.zip in the repository', $tag, self::SLUG, $version) @@ -101,21 +101,18 @@ final class GiteaUpdater } /** - * The ZIP asset of this version, only from the repository's release downloads: the trust boundary of the update. + * The ZIP of this version – its address is built here from the checked tag and the file name, not taken from the + * answer: Gitea behind a TLS proxy reports http:// (its ROOT_URL), and a URL from the answer would be a second + * trust boundary. The asset only has to exist. * * @param array $assets */ - private static function packageUrl(array $assets, string $version): ?string + private static function packageUrl(array $assets, string $tag, string $version): ?string { + $zipName = self::SLUG . '-' . $version . '.zip'; foreach ($assets as $asset) { - if (! is_array($asset)) { - continue; - } - - $url = (string) ($asset['browser_download_url'] ?? ''); - $isZip = ($asset['name'] ?? '') === self::SLUG . '-' . $version . '.zip'; - if ($isZip && str_starts_with($url, self::RELEASE_DOWNLOADS)) { - return $url; + if (is_array($asset) && ($asset['name'] ?? '') === $zipName) { + return self::RELEASE_DOWNLOADS . rawurlencode($tag) . '/' . $zipName; } } diff --git a/tests/Unit/Updater/GiteaUpdaterTest.php b/tests/Unit/Updater/GiteaUpdaterTest.php index eab6b98..07ba2fa 100644 --- a/tests/Unit/Updater/GiteaUpdaterTest.php +++ b/tests/Unit/Updater/GiteaUpdaterTest.php @@ -55,6 +55,28 @@ final class GiteaUpdaterTest extends TestCase self::assertSame('', $this->log()); } + /** @dataProvider reportedAddresses */ + public function testPackageAddressIsBuiltHereNotTakenFromTheAnswer(string $reported): void + { + $this->givenRelease(200, self::releaseBody('v1.0.0', self::ZIP_NAME, $reported)); + + $update = $this->updater()->offer(false, [], self::BASENAME); + + self::assertSame(self::ZIP, $update['package']); + } + + /** @return array */ + public static function reportedAddresses(): array + { + return [ + 'http, as Gitea behind a TLS proxy reports it' => [ + 'http://' . GiteaUpdater::HOST . '/janniclas.loosen/WP-LIT-MCP/releases/download/v1.0.0/' + . self::ZIP_NAME, + ], + 'another host' => ['https://evil.example/' . self::ZIP_NAME], + ]; + } + public function testOtherPluginsAreLeftAlone(): void { Functions\expect('wp_remote_get')->never(); @@ -104,8 +126,6 @@ final class GiteaUpdaterTest extends TestCase /** @return array */ public static function unusableReleases(): array { - $foreign = 'https://evil.example/releases/download/v1.0.0/' . self::ZIP_NAME; - return [ 'no JSON' => [''], 'tag is no version' => [self::releaseBody('latest', self::ZIP_NAME, self::ZIP)], @@ -113,10 +133,7 @@ final class GiteaUpdaterTest extends TestCase 'tag with trailing newline' => [self::releaseBody("v1.0.0\n", self::ZIP_NAME, self::ZIP)], 'asset missing' => [self::releaseBody('v1.0.0', 'other.zip', self::ZIP)], 'release without assets' => ['{"tag_name":"v1.0.0"}'], - 'package from a foreign host' => [self::releaseBody('v1.0.0', self::ZIP_NAME, $foreign)], - 'package outside the release downloads' => [ - self::releaseBody('v1.0.0', self::ZIP_NAME, 'https://' . GiteaUpdater::HOST . '/other/repo/x.zip'), - ], + 'asset of another version' => [self::releaseBody('v1.0.0', 'wp-lit-mcp-0.9.0.zip', self::ZIP)], ]; } diff --git a/wp-lit-mcp.php b/wp-lit-mcp.php index 33fc3df..8eb9203 100644 --- a/wp-lit-mcp.php +++ b/wp-lit-mcp.php @@ -3,7 +3,7 @@ /** * Plugin Name: WP-LIT-MCP * Description: MCP-Server für WordPress, Divi 5 und Pods – mit OAuth für KI-Assistenten. - * Version: 1.0.1 + * Version: 1.0.2 * Update URI: https://git.loosen-it.de/janniclas.loosen/WP-LIT-MCP * Requires at least: 6.9 * Requires PHP: 8.1