From adbfa5dd3aa52ebaf871091de3878da75b3dc96b Mon Sep 17 00:00:00 2001 From: Yahnis Elsts Date: Tue, 6 Sep 2022 15:13:15 +0300 Subject: [PATCH] Add a way to filter VCS update detection strategies Example: ``` $bitbucketPluginChecker->addFilter('vcs_update_detection_strategies', function($strategies) { //Don't look for a "Stable tag" header in readme.txt. unset($strategies['stable_tag']); return $strategies; }); ``` To make this possible, the chooseReference() method was refactored into something that more closely resembles a "chain of responsibility" pattern. Instead of a tree of "if" conditions, it now gets an array of callables from another method, and it calls each of those in order until it gets a non-empty VCS reference. You can filter this array to remove specific strategies, or even to add your own. Note that the item order matters. Required PHP version was increased to 5.4 because some "strategies" take an argument and some don't, and I would rather just use closures for that than something more complex. Coincidentally, testing this change revealed a bug where the HTTP filter name was not initialized correctly: it was missing the $slug. That should also be fixed now. Prompted by #378 --- Puc/v4p13/Vcs/Api.php | 51 +++++++++++++++++++++++++-- Puc/v4p13/Vcs/BitBucketApi.php | 33 +++++++---------- Puc/v4p13/Vcs/GitHubApi.php | 29 ++++++--------- Puc/v4p13/Vcs/GitLabApi.php | 29 ++++++--------- Puc/v4p13/Vcs/PluginUpdateChecker.php | 3 +- Puc/v4p13/Vcs/ThemeUpdateChecker.php | 3 +- composer.json | 2 +- 7 files changed, 88 insertions(+), 62 deletions(-) diff --git a/Puc/v4p13/Vcs/Api.php b/Puc/v4p13/Vcs/Api.php index 1e28b65..018ded4 100644 --- a/Puc/v4p13/Vcs/Api.php +++ b/Puc/v4p13/Vcs/Api.php @@ -2,6 +2,11 @@ if ( !class_exists('Puc_v4p13_Vcs_Api') ): abstract class Puc_v4p13_Vcs_Api { + const STRATEGY_LATEST_RELEASE = 'latest_release'; + const STRATEGY_LATEST_TAG = 'latest_tag'; + const STRATEGY_STABLE_TAG = 'stable_tag'; + const STRATEGY_BRANCH = 'branch'; + protected $tagNameProperty = 'name'; protected $slug = ''; @@ -21,6 +26,12 @@ if ( !class_exists('Puc_v4p13_Vcs_Api') ): */ protected $httpFilterName = ''; + /** + * @var string The filter applied to the list of update detection strategies that + * are used to find the latest version. + */ + protected $strategyFilterName = ''; + /** * @var string|null */ @@ -45,12 +56,41 @@ if ( !class_exists('Puc_v4p13_Vcs_Api') ): } /** - * Figure out which reference (i.e tag or branch) contains the latest version. + * Figure out which reference (i.e. tag or branch) contains the latest version. * * @param string $configBranch Start looking in this branch. * @return null|Puc_v4p13_Vcs_Reference */ - abstract public function chooseReference($configBranch); + public function chooseReference($configBranch) { + $strategies = $this->getUpdateDetectionStrategies($configBranch); + + if ( !empty($this->strategyFilterName) ) { + $strategies = apply_filters( + $this->strategyFilterName, + $strategies, + $this->slug + ); + } + + foreach ($strategies as $strategy) { + $reference = call_user_func($strategy); + if ( !empty($reference) ) { + return $reference; + } + } + return null; + } + + /** + * Get an ordered list of strategies that can be used to find the latest version. + * + * The update checker will try each strategy in order until one of them + * returns a valid reference. + * + * @param string $configBranch + * @return array Array of callables that return Vcs_Reference objects. + */ + abstract protected function getUpdateDetectionStrategies($configBranch); /** * Get the readme.txt file from the remote repository and parse it @@ -280,6 +320,13 @@ if ( !class_exists('Puc_v4p13_Vcs_Api') ): $this->httpFilterName = $filterName; } + /** + * @param string $filterName + */ + public function setStrategyFilterName($filterName) { + $this->strategyFilterName = $filterName; + } + /** * @param string $directory */ diff --git a/Puc/v4p13/Vcs/BitBucketApi.php b/Puc/v4p13/Vcs/BitBucketApi.php index 1092b40..93f0996 100644 --- a/Puc/v4p13/Vcs/BitBucketApi.php +++ b/Puc/v4p13/Vcs/BitBucketApi.php @@ -29,28 +29,21 @@ if ( !class_exists('Puc_v4p13_Vcs_BitBucketApi', false) ): parent::__construct($repositoryUrl, $credentials); } - /** - * Figure out which reference (i.e tag or branch) contains the latest version. - * - * @param string $configBranch Start looking in this branch. - * @return null|Puc_v4p13_Vcs_Reference - */ - public function chooseReference($configBranch) { - $updateSource = null; + protected function getUpdateDetectionStrategies($configBranch) { + $strategies = array( + self::STRATEGY_STABLE_TAG => function () use ($configBranch) { + return $this->getStableTag($configBranch); + }, + ); - //Check if there's a "Stable tag: 1.2.3" header that points to a valid tag. - $updateSource = $this->getStableTag($configBranch); - - //Look for version-like tags. - if ( !$updateSource && ($configBranch === 'master' || $configBranch === 'main') ) { - $updateSource = $this->getLatestTag(); - } - //If all else fails, use the specified branch itself. - if ( !$updateSource ) { - $updateSource = $this->getBranch($configBranch); + if ( ($configBranch === 'master' || $configBranch === 'main') ) { + $strategies[self::STRATEGY_LATEST_TAG] = array($this, 'getLatestTag'); } - return $updateSource; + $strategies[self::STRATEGY_BRANCH] = function () use ($configBranch) { + return $this->getBranch($configBranch); + }; + return $strategies; } public function getBranch($branchName) { @@ -261,7 +254,7 @@ if ( !class_exists('Puc_v4p13_Vcs_BitBucketApi', false) ): public function signDownloadUrl($url) { //Add authentication data to download URLs. Since OAuth signatures incorporate - //timestamps, we have to do this immediately before inserting the update. Otherwise + //timestamps, we have to do this immediately before inserting the update. Otherwise, //authentication could fail due to a stale timestamp. if ( $this->oauth ) { $url = $this->oauth->sign($url); diff --git a/Puc/v4p13/Vcs/GitHubApi.php b/Puc/v4p13/Vcs/GitHubApi.php index 0fb0b05..ba9ee2b 100644 --- a/Puc/v4p13/Vcs/GitHubApi.php +++ b/Puc/v4p13/Vcs/GitHubApi.php @@ -310,29 +310,22 @@ if ( !class_exists('Puc_v4p13_Vcs_GitHubApi', false) ): add_filter('upgrader_pre_download', array($this, 'addHttpRequestFilter'), 10, 1); //WP 3.7+ } - /** - * Figure out which reference (i.e tag or branch) contains the latest version. - * - * @param string $configBranch Start looking in this branch. - * @return null|Puc_v4p13_Vcs_Reference - */ - public function chooseReference($configBranch) { - $updateSource = null; + protected function getUpdateDetectionStrategies($configBranch) { + $strategies = array(); if ( $configBranch === 'master' ) { //Use the latest release. - $updateSource = $this->getLatestRelease(); - if ( $updateSource === null ) { - //Failing that, use the tag with the highest version number. - $updateSource = $this->getLatestTag(); - } - } - //Alternatively, just use the branch itself. - if ( empty($updateSource) ) { - $updateSource = $this->getBranch($configBranch); + $strategies[self::STRATEGY_LATEST_RELEASE] = array($this, 'getLatestRelease'); + //Failing that, use the tag with the highest version number. + $strategies[self::STRATEGY_LATEST_TAG] = array($this, 'getLatestTag'); } - return $updateSource; + //Alternatively, just use the branch itself. + $strategies[self::STRATEGY_BRANCH] = function() use ($configBranch) { + return $this->getBranch($configBranch); + }; + + return $strategies; } /** diff --git a/Puc/v4p13/Vcs/GitLabApi.php b/Puc/v4p13/Vcs/GitLabApi.php index cd90ec2..cb42797 100644 --- a/Puc/v4p13/Vcs/GitLabApi.php +++ b/Puc/v4p13/Vcs/GitLabApi.php @@ -356,28 +356,19 @@ if ( !class_exists('Puc_v4p13_Vcs_GitLabApi', false) ): throw new LogicException('The ' . __METHOD__ . ' method is not implemented and should not be used.'); } - /** - * Figure out which reference (i.e tag or branch) contains the latest version. - * - * @param string $configBranch Start looking in this branch. - * @return null|Puc_v4p13_Vcs_Reference - */ - public function chooseReference($configBranch) { + protected function getUpdateDetectionStrategies($configBranch) { + $strategies = array(); - if ( $configBranch === 'main' || $configBranch === 'master' ) { - //Use the latest release. - $updateSource = $this->getLatestRelease(); - if ( $updateSource === null ) { - //Failing that, use the tag with the highest version number. - $updateSource = $this->getLatestTag(); - } - } - //Alternatively, just use the branch itself. - if ( empty($updateSource) ) { - $updateSource = $this->getBranch($configBranch); + if ( ($configBranch === 'main') || ($configBranch === 'master') ) { + $strategies[self::STRATEGY_LATEST_RELEASE] = array($this, 'getLatestRelease'); + $strategies[self::STRATEGY_LATEST_TAG] = array($this, 'getLatestTag'); } - return $updateSource; + $strategies[self::STRATEGY_BRANCH] = function() use ($configBranch) { + return $this->getBranch($configBranch); + }; + + return $strategies; } public function setAuthentication($credentials) { diff --git a/Puc/v4p13/Vcs/PluginUpdateChecker.php b/Puc/v4p13/Vcs/PluginUpdateChecker.php index 33d9142..8bdaa7a 100644 --- a/Puc/v4p13/Vcs/PluginUpdateChecker.php +++ b/Puc/v4p13/Vcs/PluginUpdateChecker.php @@ -24,10 +24,11 @@ if ( !class_exists('Puc_v4p13_Vcs_PluginUpdateChecker') ): */ public function __construct($api, $pluginFile, $slug = '', $checkPeriod = 12, $optionName = '', $muPluginFile = '') { $this->api = $api; - $this->api->setHttpFilterName($this->getUniqueName('request_info_options')); parent::__construct($api->getRepositoryUrl(), $pluginFile, $slug, $checkPeriod, $optionName, $muPluginFile); + $this->api->setHttpFilterName($this->getUniqueName('request_info_options')); + $this->api->setStrategyFilterName($this->getUniqueName('vcs_update_detection_strategies')); $this->api->setSlug($this->slug); } diff --git a/Puc/v4p13/Vcs/ThemeUpdateChecker.php b/Puc/v4p13/Vcs/ThemeUpdateChecker.php index c463b8e..07efbe7 100644 --- a/Puc/v4p13/Vcs/ThemeUpdateChecker.php +++ b/Puc/v4p13/Vcs/ThemeUpdateChecker.php @@ -24,10 +24,11 @@ if ( !class_exists('Puc_v4p13_Vcs_ThemeUpdateChecker', false) ): */ public function __construct($api, $stylesheet = null, $customSlug = null, $checkPeriod = 12, $optionName = '') { $this->api = $api; - $this->api->setHttpFilterName($this->getUniqueName('request_update_options')); parent::__construct($api->getRepositoryUrl(), $stylesheet, $customSlug, $checkPeriod, $optionName); + $this->api->setHttpFilterName($this->getUniqueName('request_update_options')); + $this->api->setStrategyFilterName($this->getUniqueName('vcs_update_detection_strategies')); $this->api->setSlug($this->slug); } diff --git a/composer.json b/composer.json index 7f97a49..0b1c37b 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ } ], "require": { - "php": ">=5.2.0", + "php": ">=5.4.0", "ext-json": "*" }, "autoload": {