From af5086c30565ed6dfc2f127d49dc2a840f4476e5 Mon Sep 17 00:00:00 2001 From: Mehdi GHESH Date: Fri, 13 Sep 2024 14:18:58 +0200 Subject: [PATCH 01/10] Add the server's update center url --- jenkinsapi/jenkins.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jenkinsapi/jenkins.py b/jenkinsapi/jenkins.py index 2ed75b72..c431324e 100644 --- a/jenkinsapi/jenkins.py +++ b/jenkinsapi/jenkins.py @@ -393,6 +393,9 @@ def get_plugins_url(self, depth): # This only ever needs to work on the base object return f"{self.baseurl}/pluginManager/api/python?depth={depth}" + def get_update_center_url(self, depth = 1): + return f"{self.baseurl}/manage/updateCenter/api/json?depth={depth}" + def install_plugin( self, plugin: str | Plugin, From 08bb79b2fb2f971150f902e157e44ca3829201be Mon Sep 17 00:00:00 2001 From: Mehdi GHESH Date: Fri, 13 Sep 2024 14:20:24 +0200 Subject: [PATCH 02/10] Update type and documentation about what plugin variable can really be --- jenkinsapi/jenkins.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/jenkinsapi/jenkins.py b/jenkinsapi/jenkins.py index c431324e..366c1857 100644 --- a/jenkinsapi/jenkins.py +++ b/jenkinsapi/jenkins.py @@ -398,7 +398,7 @@ def get_update_center_url(self, depth = 1): def install_plugin( self, - plugin: str | Plugin, + plugin: str | dict | Plugin, restart: bool = True, force_restart: bool = False, wait_for_reboot: bool = True, @@ -406,7 +406,7 @@ def install_plugin( ): """ Install a plugin and optionally restart jenkins. - @param plugin: Plugin (string or Plugin object) to be installed + @param plugin: Plugin (string or dict or Plugin object) to be installed @param restart: Boolean, restart Jenkins when required by plugin @param force_restart: Boolean, force Jenkins to restart, ignoring plugin preferences @@ -649,7 +649,14 @@ def plugins(self): def get_plugins(self, depth: int = 1) -> Plugins: url = self.get_plugins_url(depth=depth) - return Plugins(url, self) + # If the plugins object is not already created or the baseurl has changed + # then we recreate a new one + if ( + not hasattr(self, "_get_plugins") + or self._plugin_cache.baseurl != url + ): + self._plugin_cache = Plugins(url, self) + return self._plugin_cache def has_plugin(self, plugin_name: str) -> bool: return plugin_name in self.plugins From 62bb63e4d2f69990f2621161f449b34535ba5d1c Mon Sep 17 00:00:00 2001 From: Mehdi GHESH Date: Fri, 13 Sep 2024 14:23:23 +0200 Subject: [PATCH 03/10] Upload node configs with an xml content type --- jenkinsapi/node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jenkinsapi/node.py b/jenkinsapi/node.py index 283a9704..a53332ea 100644 --- a/jenkinsapi/node.py +++ b/jenkinsapi/node.py @@ -358,7 +358,7 @@ def upload_config(self, config_xml: str) -> None: if self.name == "Built-In Node": raise JenkinsAPIException("Built-In node does not have config.xml") - self.jenkins.requester.post_and_confirm_status( + self.jenkins.requester.post_xml_and_confirm_status( "%(baseurl)s/config.xml" % self.__dict__, data=config_xml ) From aa5c713a1da31a2271f05d1508060d0e37a467ca Mon Sep 17 00:00:00 2001 From: Mehdi GHESH Date: Fri, 13 Sep 2024 14:25:00 +0200 Subject: [PATCH 04/10] Introduce url field, to save times when we already know where the plugin should be downloaded from --- jenkinsapi/plugin.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jenkinsapi/plugin.py b/jenkinsapi/plugin.py index 17b40559..5d427803 100644 --- a/jenkinsapi/plugin.py +++ b/jenkinsapi/plugin.py @@ -19,6 +19,7 @@ def __init__(self, plugin_dict: Union[dict, str]) -> None: self.__dict__ = self.to_plugin(plugin_dict) self.shortName: str = self.__dict__["shortName"] self.version: str = self.__dict__.get("version", "Unknown") + self.url: str = self.__dict__.get("url", None) def to_plugin(self, plugin_string: str) -> dict: plugin_string = str(plugin_string) @@ -68,6 +69,9 @@ def is_latest(self, update_center_dict: dict) -> bool: return center_plugin["version"] == self.version def get_download_link(self, update_center_dict) -> str: + if self.url: + return self.url + latest_version = update_center_dict["plugins"][self.shortName][ "version" ] From 1b91b2c92f9618be5b4bd88300acb30bbe776321 Mon Sep 17 00:00:00 2001 From: Mehdi GHESH Date: Fri, 13 Sep 2024 14:27:27 +0200 Subject: [PATCH 05/10] Enhance the string representation of a plugin by displaying the version as well --- jenkinsapi/plugin.py | 2 +- jenkinsapi_tests/unittests/test_plugins.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jenkinsapi/plugin.py b/jenkinsapi/plugin.py index 5d427803..393077a0 100644 --- a/jenkinsapi/plugin.py +++ b/jenkinsapi/plugin.py @@ -38,7 +38,7 @@ def __eq__(self, other) -> bool: return self.__dict__ == other.__dict__ def __str__(self) -> str: - return self.shortName + return f"{self.shortName}@{self.version}" def __repr__(self) -> str: return "<%s.%s %s>" % ( diff --git a/jenkinsapi_tests/unittests/test_plugins.py b/jenkinsapi_tests/unittests/test_plugins.py index c96a0508..02c21a07 100644 --- a/jenkinsapi_tests/unittests/test_plugins.py +++ b/jenkinsapi_tests/unittests/test_plugins.py @@ -362,7 +362,7 @@ def test_plugin_repr(self): "shortName": "subversion", } ) - self.assertEqual(repr(p), "") + self.assertEqual(repr(p), "") if __name__ == "__main__": From 6832c3136f5d00f38df8ea3ea7070fcb47ea4474 Mon Sep 17 00:00:00 2001 From: Mehdi GHESH Date: Fri, 13 Sep 2024 14:31:01 +0200 Subject: [PATCH 06/10] Add a method to check if installation is successful for all plugins --- jenkinsapi/plugins.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/jenkinsapi/plugins.py b/jenkinsapi/plugins.py index 8e3dc6ce..572905b3 100644 --- a/jenkinsapi/plugins.py +++ b/jenkinsapi/plugins.py @@ -209,6 +209,24 @@ def _plugin_has_finished_installation(self, plugin) -> bool: except JenkinsAPIException: return False # lack of update_center in Jenkins 1.X + def _plugins_has_finished_installation(self) -> bool: + """ + Return True if installation is marked as 'Success' or + 'SuccessButRequiresRestart' in Jenkins' update_center, + else return False. + """ + try: + jobs = self.update_center_install_status["data"]["jobs"] + for job in jobs: + if job["installStatus"] not in [ + "Success", + "SuccessButRequiresRestart", + ]: + return False + return True + except JenkinsAPIException: + return False # lack of update_center in Jenkins 1.X + def plugin_version_is_being_installed(self, plugin) -> bool: """ Return true if plugin is currently being installed. From fcd36cc1c6e2021c0210576662c8277d439e885b Mon Sep 17 00:00:00 2001 From: Mehdi GHESH Date: Tue, 24 Sep 2024 12:09:19 +0200 Subject: [PATCH 07/10] Provide info to jenkins instead of local downloading the plugin --- jenkinsapi/plugins.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/jenkinsapi/plugins.py b/jenkinsapi/plugins.py index 572905b3..7c6088d5 100644 --- a/jenkinsapi/plugins.py +++ b/jenkinsapi/plugins.py @@ -145,16 +145,11 @@ def _install_specific_version(self, plugin: "Plugin") -> None: download_link: str = plugin.get_download_link( update_center_dict=self.update_center_dict ) - downloaded_plugin: BytesIO = self._download_plugin(download_link) - plugin_dependencies = self._get_plugin_dependencies(downloaded_plugin) - log.debug("Installing dependencies for plugin '%s'", plugin.shortName) - self.jenkins_obj.install_plugins(plugin_dependencies) url = "%s/pluginManager/uploadPlugin" % self.jenkins_obj.baseurl requester = self.jenkins_obj.requester - downloaded_plugin.seek(0) requester.post_and_confirm_status( url, - files={"file": ("plugin.hpi", downloaded_plugin)}, + files={"filename": plugin.shortName, "pluginUrl": download_link}, data={}, params={}, ) From 51a8e30de858478a57a69e46f9675f01eb8f7b67 Mon Sep 17 00:00:00 2001 From: Mehdi GHESH Date: Tue, 24 Sep 2024 12:09:36 +0200 Subject: [PATCH 08/10] Cache the center dict content to save time --- jenkinsapi/plugins.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/jenkinsapi/plugins.py b/jenkinsapi/plugins.py index 7c6088d5..4371340a 100644 --- a/jenkinsapi/plugins.py +++ b/jenkinsapi/plugins.py @@ -46,9 +46,12 @@ def check_updates_server(self) -> None: @property def update_center_dict(self): - update_center = "https://updates.jenkins.io/update-center.json" - jsonp = requests.get(update_center).content.decode("utf-8") - return json.loads(jsonp_to_json(jsonp)) + if not hasattr(self, "_update_center_dict"): + update_center = ( + "https://updates.jenkins.io/update-center.actual.json" + ) + self._update_center_dict = requests.get(update_center).json() + return self._update_center_dict def _poll(self, tree=None): return self.get_data(self.baseurl, tree=tree) From 3a3c3e4132cc7f13cbdb04bc76e2233149982b6f Mon Sep 17 00:00:00 2001 From: Mehdi GHESH Date: Tue, 24 Sep 2024 12:10:07 +0200 Subject: [PATCH 09/10] Add the server specific center dict, which may differ from the cloud one --- jenkinsapi/plugins.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/jenkinsapi/plugins.py b/jenkinsapi/plugins.py index 4371340a..3eaf3544 100644 --- a/jenkinsapi/plugins.py +++ b/jenkinsapi/plugins.py @@ -53,6 +53,14 @@ def update_center_dict(self): self._update_center_dict = requests.get(update_center).json() return self._update_center_dict + @property + def update_center_dict_server(self): + if not hasattr(self, "_update_center_dict_server"): + self._update_center_dict_server = self.jenkins_obj.requester.get_url( + self.jenkins_obj.get_update_center_url(2) + ).json() + return self._update_center_dict_server + def _poll(self, tree=None): return self.get_data(self.baseurl, tree=tree) From f330e0df08bc8dff91e653561cdd26a31dfb6c78 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 09:10:40 +0000 Subject: [PATCH 10/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- jenkinsapi/jenkins.py | 2 +- jenkinsapi/plugins.py | 8 +++++--- jenkinsapi_tests/unittests/test_plugins.py | 4 +++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/jenkinsapi/jenkins.py b/jenkinsapi/jenkins.py index 366c1857..2cefbd65 100644 --- a/jenkinsapi/jenkins.py +++ b/jenkinsapi/jenkins.py @@ -393,7 +393,7 @@ def get_plugins_url(self, depth): # This only ever needs to work on the base object return f"{self.baseurl}/pluginManager/api/python?depth={depth}" - def get_update_center_url(self, depth = 1): + def get_update_center_url(self, depth=1): return f"{self.baseurl}/manage/updateCenter/api/json?depth={depth}" def install_plugin( diff --git a/jenkinsapi/plugins.py b/jenkinsapi/plugins.py index 3eaf3544..524a17cc 100644 --- a/jenkinsapi/plugins.py +++ b/jenkinsapi/plugins.py @@ -56,9 +56,11 @@ def update_center_dict(self): @property def update_center_dict_server(self): if not hasattr(self, "_update_center_dict_server"): - self._update_center_dict_server = self.jenkins_obj.requester.get_url( - self.jenkins_obj.get_update_center_url(2) - ).json() + self._update_center_dict_server = ( + self.jenkins_obj.requester.get_url( + self.jenkins_obj.get_update_center_url(2) + ).json() + ) return self._update_center_dict_server def _poll(self, tree=None): diff --git a/jenkinsapi_tests/unittests/test_plugins.py b/jenkinsapi_tests/unittests/test_plugins.py index 02c21a07..2bc6ff53 100644 --- a/jenkinsapi_tests/unittests/test_plugins.py +++ b/jenkinsapi_tests/unittests/test_plugins.py @@ -362,7 +362,9 @@ def test_plugin_repr(self): "shortName": "subversion", } ) - self.assertEqual(repr(p), "") + self.assertEqual( + repr(p), "" + ) if __name__ == "__main__":