nur-sery/nur_src/tools/compctl/ComposerFile.php

211 lines
6.5 KiB
PHP
Raw Normal View History

2023-12-03 22:10:18 +04:00
<?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 composeruration n'a été trouvée";
throw new ValueException($message);
}
$this->composerFile = $composerFile;
}
/** @var string */
protected $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];
}
function sync(ConfigFile $config, string $branch, bool $inplace=false): bool {
[$modified, $json] = $this->computeComposer($config, $branch);
$contents = json::with($json, json::INDENT_TABS);
if ($inplace) {
if ($modified) file::with($this->composerFile)->putContents($contents);
else msg::info("Aucune modifications effectuée");
} else {
if ($contents !== null) echo "$contents\n";
}
return $modified;
}
}