From e83cf1f9095b2b3e036881ed2edb4ec78417e408 Mon Sep 17 00:00:00 2001 From: SONOLET Aymeric Date: Mon, 4 Dec 2023 15:41:51 +0100 Subject: [PATCH] Create UpdateOp --- commands/update.py | 287 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 commands/update.py diff --git a/commands/update.py b/commands/update.py new file mode 100644 index 0000000..cc6daef --- /dev/null +++ b/commands/update.py @@ -0,0 +1,287 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- +# Copyright (C) 2010-2012 CEA/DEN +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import re +import os +import pprint as PP + +import src +import src.debug as DBG + + +# Define all possible option for update command : sat update +parser = src.options.Options() +parser.add_option( + "p", + "products", + "list2", + "products", + _("Optional: products to update. This option accepts a comma separated list."), +) +# parser.add_option('f', 'force', 'boolean', 'force', +# _("Optional: force to update the products in development mode.")) +parser.add_option('', 'force_patch', 'boolean', 'force_patch', + _("Optional: force to apply patch to the products in development mode.")) +# parser.add_option('c', 'complete', 'boolean', 'complete', +# _("Optional: completion mode, only update products not present in SOURCES dir."), +# False) + + +def find_products_already_prepared(l_products): + """function that returns the list of products that have an existing source + directory. + + :param l_products List: The list of products to check + :return: The list of product configurations that have an existing source + directory. + :rtype: List + """ + l_res = [] + for p_name_p_cfg in l_products: + __, prod_cfg = p_name_p_cfg + if "source_dir" in prod_cfg and os.path.exists(prod_cfg.source_dir): + l_res.append(p_name_p_cfg) + return l_res + +def find_git_products(l_products): + """ + function that returns the list of products that have an existing source + directory and a git configuration. Those products will be updated using : + + git checkout TARGET_TAG + git pull origin TARGET_TAG --ff-only + + Not committed dev or conflict with origin during pull will trigger an error. + + :param l_products List: The list of products to check + :return: The list of product configurations that have an existing source + directory and a git history. + :rtype: List + """ + l_res = [] + for p_name_p_cfg in l_products: + __, prod_cfg = p_name_p_cfg + if "source_dir" in prod_cfg and os.path.exists(prod_cfg.source_dir): + if os.path.exists(prod_cfg.source_dir + "/.git"): + l_res.append(p_name_p_cfg) + return l_res + + +def find_products_with_patchs(l_products): + """function that returns the list of products that have one or more patches. + + :param l_products List: The list of products to check + :return: The list of product configurations that have one or more patches. + :rtype: List + """ + l_res = [] + for p_name_p_cfg in l_products: + __, prod_cfg = p_name_p_cfg + l_patchs = src.get_cfg_param(prod_cfg, "patches", []) + if len(l_patchs) > 0: + l_res.append(p_name_p_cfg) + return l_res + + +def description(): + """method that is called when salomeTools is called with --help option. + + :return: The text to display for the update command description. + :rtype: str + """ + return _( + "The update command gets the sources of " + "the application products and apply the patches if there is any." + "\n\nexample:\nsat update SALOME-master --products KERNEL,GUI" + ) + +class UpdateOp: + """ + This is an operation class. It is prepared though the init and launched + with the launch method. + + This operation updates the products, meaning it get the missing ones, and + pull the TARGET_TAG for the already present ones. It prevents from erasing + everything, especially .git/ files. + + In case you have uncommited work, the operation will stop. + + In case the remote tracking branch can't be pulled fast-forward (after a + checkout to the matching local branch), the operation will stop. + """ + + def __init__(self, args, runner, logger): + """ + Initialisation of the UpdateOp. The runner and the plateform are + checked. + + :args: arguments passed to sat + :runner: Sat class instance + :logger: Current logger + """ + # check that the command has been called with an application + src.check_config_has_application(runner.cfg) + + # write warning if platform is not declared as supported + src.check_platform_is_supported(runner.cfg, logger) + + # Parse the options + (options, args) = parser.parse_args(args) + self._list_of_products = options.products + self._force_patch = options.force_patch + + self.runner = runner + self.logger = logger + self.products_infos = src.product.get_products_list(options, self.runner.cfg, self.logger) + + # Construct the arguments to pass to the clean, source and patch commands + self.args_appli = runner.cfg.VARS.application + " " # useful whitespace + + @property + def products(self): + if self._list_of_products: + return list(self._list_of_products) + return [name for name, tmp in self.products_infos] + + def getProductsToPrepare(self): + """ + Remove products that are already prepared and under git tracking so + that only new products (and not tracked ones) are prepared. + """ + pi_already_prepared = find_git_products(self.products_infos) + l_already_prepared = [i for i, tmp in pi_already_prepared] + newList, removedList = removeInList(self.products, l_already_prepared) + if len(newList) == 0 and len(removedList) > 0: + msg = "\nAll the products are already installed, do nothing!\n" + self.logger.write(src.printcolors.printcWarning(msg), 1) + return 0 + if len(removedList) > 0: + msg = ( + "\nList of already prepared products that are skipped : %s\n" + % ",".join(removedList) + ) + self.logger.write(msg, 3) + return newList + + def getProductsToUpdate(self): + pi_already_prepared = find_git_products(self.products_infos) + productsToUpdate = [i for i, tmp in pi_already_prepared] + return productsToUpdate + + def getProductsToClean(self, listProdToPrepare): + ldev_products = [p for p in self.products_infos if src.product.product_is_dev(p[1])] + productsToClean = listProdToPrepare # default + if len(ldev_products) > 0: + l_products_not_getted = find_products_already_prepared(ldev_products) + listNot = [i for i, tmp in l_products_not_getted] + productsToClean, removedList = removeInList(listProdToPrepare, listNot) + if len(removedList) > 0: + msg = _( + """ + Do not get the source of the following products in + development mode. + """ + ) + msg += "\n%s\n" % ",".join(removedList) + self.logger.write(src.printcolors.printcWarning(msg), 1) + return productsToClean + + def getProductsToPatch(self, listProdToPrepare): + productsToPatch = listProdToPrepare # default + ldev_products = [p for p in self.products_infos if src.product.product_is_dev(p[1])] + if not self._force_patch and len(ldev_products) > 0: + l_products_with_patchs = find_products_with_patchs(ldev_products) + listNot = [i for i, tmp in l_products_with_patchs] + productsToPatch, removedList = removeInList(listProdToPrepare, listNot) + if len(removedList) > 0: + msg = _( + """ + Do not patch the following products in development mode. + Use the --force_patch option to overwrite it. + """ + ) + msg += "\n%s\n" % ",".join(removedList) + self.logger.write(src.printcolors.printcWarning(msg), 1) + return productsToPatch + + def launch(self): + productsToPrepare = self.getProductsToPrepare() + args_product_to_prepare_opt = "--products " + ",".join(productsToPrepare) + + productsToClean = self.getProductsToClean(productsToPrepare) + args_product_opt_clean = "--products " + ",".join(productsToClean) + + productsToPatch = self.getProductsToPatch(productsToPrepare) + args_product_opt_patch = "--products " + ",".join(productsToPatch) + + + # Initialize the results to a running status + res_clean = 0 + res_source = 0 + res_patch = 0 + + # Call the commands using the API + if len(productsToClean) > 0: + msg = _("Clean the source directories ...") + self.logger.write(msg, 3) + self.logger.flush() + args_clean = self.args_appli + args_product_opt_clean + " --sources" + res_clean = self.runner.clean(args_clean, batch=True, verbose = 0, logger_add_link = self.logger) + if res_clean == 0: + self.logger.write('%s\n' % src.printcolors.printc(src.OK_STATUS), 3) + else: + self.logger.write('%s\n' % src.printcolors.printc(src.KO_STATUS), 3) + if len(productsToPrepare) > 0: + msg = _("Get the sources of the products ...") + self.logger.write(msg, 5) + args_source = self.args_appli + args_product_to_prepare_opt + res_source = self.runner.source(args_source, logger_add_link=self.logger) + if res_source == 0: + self.logger.write("%s\n" % src.printcolors.printc(src.OK_STATUS), 5) + else: + self.logger.write("%s\n" % src.printcolors.printc(src.KO_STATUS), 5) + if len(productsToPatch) > 0: + msg = _("Patch the product sources (if any) ...") + self.logger.write(msg, 5) + args_patch = self.args_appli + args_product_opt_patch + res_patch = self.runner.patch(args_patch, logger_add_link=self.logger) + if res_patch == 0: + self.logger.write("%s\n" % src.printcolors.printc(src.OK_STATUS), 5) + else: + self.logger.write("%s\n" % src.printcolors.printc(src.KO_STATUS), 5) + return res_clean + res_source + res_patch + + + +def run(args, runner, logger): + """method that is called when salomeTools is called with update parameter.""" + updateOp = UpdateOp(args, runner, logger) + res = updateOp.launch() + return res + + +def removeInList(aList, removeList): + """Removes elements of removeList list from aList + + :param aList: (list) The list from which to remove elements + :param removeList: (list) The list which contains elements to remove + :return: (list, list) (list with elements removed, list of elements removed) + """ + res1 = [i for i in aList if i not in removeList] + res2 = [i for i in aList if i in removeList] + return (res1, res2) -- 2.39.2