236 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			236 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| namespace nur\tools\compctl;
 | |
| 
 | |
| use nur\A;
 | |
| use nur\b\ValueException;
 | |
| use nur\file;
 | |
| use nur\json;
 | |
| use nur\msg;
 | |
| use nur\path;
 | |
| use nur\SL;
 | |
| use nur\str;
 | |
| use nur\yaml;
 | |
| 
 | |
| class ComposerFile {
 | |
|   function __construct(string $composerFile, ?string $projdir=null, bool $ensureExists=true) {
 | |
|     if ($ensureExists && !file_exists($composerFile)) {
 | |
|       if ($projdir === null) $projdir = dirname($composerFile);
 | |
|       $message = path::ppath($projdir).": aucune configuration n'a été trouvée";
 | |
|       throw new ValueException($message);
 | |
|     }
 | |
|     $this->composerFile = $composerFile;
 | |
|   }
 | |
| 
 | |
|   /** @var string */
 | |
|   protected $composerFile;
 | |
| 
 | |
|   function getComposerFile(): string {
 | |
|     return $this->composerFile;
 | |
|   }
 | |
| 
 | |
|   /** @var array */
 | |
|   protected $data;
 | |
| 
 | |
|   protected function load(): array {
 | |
|     if ($this->data === null) {
 | |
|       $this->data = json::load($this->composerFile);
 | |
|     }
 | |
|     return $this->data;
 | |
|   }
 | |
| 
 | |
|   function getv(string $pkey="", $default=null) {
 | |
|     $this->load();
 | |
|     return A::pget($this->data, $pkey, $default);
 | |
|   }
 | |
| 
 | |
|   function geta(string $pkey="", ?array $default=[]): ?array {
 | |
|     return A::withn($this->getv($pkey, $default));
 | |
|   }
 | |
| 
 | |
|   function getRequires(): array {
 | |
|     return $this->geta("require");
 | |
|   }
 | |
| 
 | |
|   function getRepositories(): array {
 | |
|     return $this->geta("repositories");
 | |
|   }
 | |
| 
 | |
|   /** construire un fichier de configuration à partir du fichier composer */
 | |
|   function initConfig(string $configFile): ?ConfigFile {
 | |
|     if (file_exists($configFile)) return null;
 | |
|     $requires = $this->getRequires();
 | |
|     $php = A::get($requires, "php", ">=7.4");
 | |
|     switch ($php) {
 | |
|     case ">=8.2":
 | |
|       $composer = [
 | |
|         "php_min" => "8.2",
 | |
|         "php_max" => "8.3",
 | |
|         "image" => "pubdocker.univ-reunion.fr/image/phpbuilder:d12",
 | |
|       ];
 | |
|       break;
 | |
|     case ">=7.4":
 | |
|       $composer = [
 | |
|         "php_min" => "7.4",
 | |
|         "php_max" => "8.0",
 | |
|         "image" => "pubdocker.univ-reunion.fr/image/phpbuilder:d11",
 | |
|       ];
 | |
|       break;
 | |
|     case ">=7.3":
 | |
|       $composer = [
 | |
|         "php_min" => "7.3",
 | |
|         "php_max" => "8.0",
 | |
|         "image" => "pubdocker.univ-reunion.fr/image/phpbuilder:d10",
 | |
|       ];
 | |
|       break;
 | |
|     case ">=7.0":
 | |
|       $composer = [
 | |
|         "php_min" => "7.0",
 | |
|         "php_max" => "7.1",
 | |
|         "image" => "pubdocker.univ-reunion.fr/image/phpbuilder:d9",
 | |
|       ];
 | |
|       break;
 | |
|     default:
 | |
|       msg::warning("$php: impossible de calculer la valeur de \$composer, choix du support de PHP 7.4");
 | |
|       $composer = [
 | |
|         "php_min" => "7.4",
 | |
|         "php_max" => "8.0",
 | |
|         "image" => "pubdocker.univ-reunion.fr/image/phpbuilder:d11",
 | |
|       ];
 | |
|       break;
 | |
|     }
 | |
|     $defaults = ConfigFile::DEFAULT_CONFIG["defaults"];
 | |
|     $require = null;
 | |
|     foreach ($requires as $libname => $version) {
 | |
|       $isNur = str::starts_with("nur/", $libname);
 | |
|       $isLib = str::starts_with("lib/", $libname);
 | |
|       if (!$isNur && !$isLib) continue;
 | |
|       if (str::starts_with("dev-", $version)
 | |
|         || str::ends_with("-dev", $version)) {
 | |
|         # version de développement, laisser se calculer les valeurs par défaut
 | |
|         $require[$libname] = null;
 | |
|       } else {
 | |
|         $require[$libname] = $version;
 | |
|       }
 | |
|     }
 | |
|     $branches = array_fill_keys(array_keys($defaults), null);
 | |
|     $config = [
 | |
|       "composer" => $composer,
 | |
|       #"defaults" => $defaults,
 | |
|       "require" => $require,
 | |
|       "branch" => $branches,
 | |
|     ];
 | |
|     $contents = yaml::with($config);
 | |
|     # pour la joliesse, enlever les ': null'
 | |
|     $contents = preg_replace('/: null$/m', ":", $contents);
 | |
| 
 | |
|     $writer = file::with($configFile)->getWriter();
 | |
|     $writer->pnl("# -*- coding: utf-8 mode: yaml -*- vim:sw=2:sts=2:et:ai:si:sta:fenc=utf-8");
 | |
|     $writer->putContents($contents);
 | |
|     return new ConfigFile($configFile);
 | |
|   }
 | |
| 
 | |
|   function computeComposer(ConfigFile $config, string $branch): array {
 | |
|     $data = $orig = $this->load();
 | |
| 
 | |
|     [$deps, $isUrls, $notFoundUrls,
 | |
|     ] = $config->getDeps($branch);
 | |
|     if ($deps === null) {
 | |
|       msg::warning("$branch: aucune définition trouvée pour cette branche");
 | |
|       return [false, $orig];
 | |
|     }
 | |
|     foreach ($notFoundUrls as $url) {
 | |
|       msg::warning("$url: project not found. not updating repositories");
 | |
|     }
 | |
| 
 | |
|     # nettoyer les chemins pour chacune des dépendances de la config
 | |
|     $crequires = A::get($data, "require", []);
 | |
|     $crepositories = A::get($data, "repositories");
 | |
|     if ($crepositories === null) {
 | |
|       $crepositories = [[
 | |
|         "type" => "composer",
 | |
|         "url" => "https://repos.univ-reunion.fr/composer",
 | |
|       ]];
 | |
|     }
 | |
|     foreach ($crepositories as $key => $crepository) {
 | |
|       if (A::get($crepository, "type") !== "path") continue;
 | |
|       $url = A::get($crepository, "url");
 | |
|       if (A::get($isUrls, $url)) {
 | |
|         unset($crepositories[$key]);
 | |
|         continue;
 | |
|       }
 | |
|       # si un projet nur/ ou lib/ se trouve dans require, le prendre en compte
 | |
|       # en d'autres termes, on nettoie les chemins qui n'ont plus de correspondance...
 | |
|       $projname = basename($url);
 | |
|       $libname = null;
 | |
|       if (str::del_prefix($projname, "nur-")) {
 | |
|         $libname = "nur/$projname";
 | |
|       } elseif (str::del_prefix($projname, "lib-")) {
 | |
|         $libname = "lib/$projname";
 | |
|       }
 | |
|       if ($libname !== null && array_key_exists($libname, $crequires)) {
 | |
|         unset($crepositories[$key]);
 | |
|       }
 | |
|     }
 | |
|     $data["repositories"] = $crepositories;
 | |
|     # puis recalculer la liste des chemins
 | |
|     $crepositories = [];
 | |
|     foreach ($deps as $dep) {
 | |
|       if ($dep["link"]) {
 | |
|         $crepositories[] = [
 | |
|           "type" => $dep["type"],
 | |
|           "url" => $dep["url"],
 | |
|         ];
 | |
|       }
 | |
|     }
 | |
|     $data["repositories"] = SL::merge($crepositories, $data["repositories"]);
 | |
|     # nettoyer les dépendances pour chacune des dépendances de la config
 | |
|     foreach ($crequires as $libname => $version) {
 | |
|       if (array_key_exists($libname, $deps)) {
 | |
|         unset($crequires[$libname]);
 | |
|       }
 | |
|     }
 | |
|     $data["require"] = $crequires;
 | |
|     # puis installer les dépendances de la config
 | |
|     $crequires = [];
 | |
|     foreach ($deps as $dep) {
 | |
|       $crequires[$dep["libname"]] = $dep["version"];
 | |
|     }
 | |
|     $data["require"] = SL::merge($crequires, $data["require"]);
 | |
|     # retourner le nouveau contenu (qu'il faudra sérialiser)
 | |
|     return [$data != $orig, $data];
 | |
|   }
 | |
| 
 | |
|   protected function _save(array $data, bool $modified, bool $rewrite, bool $forceRewrite): bool {
 | |
|     $contents = json::with($data, json::INDENT_TABS);
 | |
|     if ($rewrite) {
 | |
|       $contents .= "\n";
 | |
|       if ($modified || $forceRewrite) file::with($this->composerFile)->putContents($contents);
 | |
|       else msg::info("Aucune modifications effectuée");
 | |
|     } else {
 | |
|       if ($contents) echo "$contents\n";
 | |
|     }
 | |
|     return $modified;
 | |
|   }
 | |
| 
 | |
|   function sync(ConfigFile $config, string $branch, bool $rewrite=false, bool $forceRewrite=false): bool {
 | |
|     [$modified, $data] = $this->computeComposer($config, $branch);
 | |
|     return $this->_save($data, $modified, $rewrite, $forceRewrite);
 | |
|   }
 | |
| 
 | |
|   function setPkey(string $pkey, $value, bool $rewrite=false, bool $forceRewrite=false): bool {
 | |
|     $data = $orig = $this->load();
 | |
|     A::pset($data, $pkey, $value);
 | |
|     return $this->_save($data, $data != $orig, $rewrite, $forceRewrite);
 | |
|   }
 | |
| 
 | |
|   function delPkey(string $pkey, bool $rewrite=false, bool $forceRewrite=false): bool {
 | |
|     $data = $orig = $this->load();
 | |
|     A::pdel($data, $pkey);
 | |
|     return $this->_save($data, $data != $orig, $rewrite, $forceRewrite);
 | |
|   }
 | |
| 
 | |
|   function updateDep(string $dep, string $version, bool $rewrite=false, bool $forceRewrite=false): bool {
 | |
|     return $this->setPkey("require.$dep", $version, $rewrite, $forceRewrite);
 | |
|   }
 | |
| }
 |