maj nur-base
This commit is contained in:
parent
9415a1eb04
commit
fc6963c39e
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/php
|
||||
<?php
|
||||
require $_composer_autoload_path?? __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
\nur\tools\compctl\CompdepApp::run();
|
|
@ -3,7 +3,6 @@ namespace nur\ref;
|
|||
|
||||
use nur\data\types\Metadata;
|
||||
use nur\md;
|
||||
use nur\sery\php\content\content;
|
||||
|
||||
/**
|
||||
* Class ref_type: référence des types utilisables dans les schémas
|
||||
|
|
|
@ -8,26 +8,45 @@ class shutils {
|
|||
static function ls_all(string $dir, ?string $pattern=null, int $sorting_order=SCANDIR_SORT_ASCENDING): array {
|
||||
$all = scandir($dir, $sorting_order);
|
||||
if ($all === false) return [];
|
||||
return array_filter($all,
|
||||
return array_values(array_filter($all,
|
||||
function ($file) use ($pattern) {
|
||||
if ($file === "." || $file === "..") return false;
|
||||
return $pattern === null || fnmatch($pattern, $file);
|
||||
});
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
static function ls_dirs(string $dir, ?string $pattern=null, int $sorting_order=SCANDIR_SORT_ASCENDING): array {
|
||||
return array_filter(self::ls_all($dir, $pattern, $sorting_order),
|
||||
return array_values(array_filter(self::ls_all($dir, $pattern, $sorting_order),
|
||||
function ($file) use ($dir) {
|
||||
return path::is_dir(path::join($dir, $file));
|
||||
}
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
static function ls_files(string $dir, ?string $pattern=null, int $sorting_order=SCANDIR_SORT_ASCENDING): array {
|
||||
return array_filter(self::ls_all($dir, $pattern, $sorting_order),
|
||||
return array_values(array_filter(self::ls_all($dir, $pattern, $sorting_order),
|
||||
function ($file) use ($dir) {
|
||||
return path::is_file(path::join($dir, $file));
|
||||
}
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
static function ls_pall(string $dir, ?string $pattern=null, int $sorting_order=SCANDIR_SORT_ASCENDING): array {
|
||||
return array_map(function(string $name) use ($dir) {
|
||||
return path::join($dir, $name);
|
||||
}, self::ls_all($dir, $pattern, $sorting_order));
|
||||
}
|
||||
|
||||
static function ls_pdirs(string $dir, ?string $pattern=null, int $sorting_order=SCANDIR_SORT_ASCENDING): array {
|
||||
return array_map(function(string $name) use ($dir) {
|
||||
return path::join($dir, $name);
|
||||
}, self::ls_dirs($dir, $pattern, $sorting_order));
|
||||
}
|
||||
|
||||
static function ls_pfiles(string $dir, ?string $pattern=null, int $sorting_order=SCANDIR_SORT_ASCENDING): array {
|
||||
return array_map(function(string $name) use ($dir) {
|
||||
return path::join($dir, $name);
|
||||
}, self::ls_files($dir, $pattern, $sorting_order));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
namespace nur\tools\compctl;
|
||||
|
||||
use nur\A;
|
||||
use nur\cli\Application;
|
||||
use nur\msg;
|
||||
use nur\shutils;
|
||||
|
||||
class CompdepApp extends Application {
|
||||
private static function split_pkey_value(string $pv): array {
|
||||
if (preg_match('/([a-zA-Z0-9._-]+)=(.*)/', $pv, $ms)) {
|
||||
return [$ms[1], $ms[2]];
|
||||
} else {
|
||||
return [$pv, true];
|
||||
}
|
||||
}
|
||||
|
||||
const SHOW_NAMES = "names", SHOW_DEPS = "deps", SHOW_DIRS = "dirs", SHOW_COMPOSER = "composer";
|
||||
const ARGS = [
|
||||
"purpose" => "Lister les dépendances d'un projet PHP",
|
||||
|
||||
["-B", "--projects-dir", "args" => "dir",
|
||||
"help" => "Spécifier le répertoire de base des projets",
|
||||
],
|
||||
["-w", "--rewrite",
|
||||
"help" => "modifier le fichier composer.json au lieu d'afficher le nouveau contenu",
|
||||
],
|
||||
["--force-rewrite",
|
||||
"help" => "en cas de modification, forcer la ré-écriture du fichier composer.json même s'il est inchangé",
|
||||
],
|
||||
["-s", "--set-pkey", "args" => 1, "argsdesc" => "PKEY=VALUE",
|
||||
"help" => "créer ou modifier le chemin de clé avec la valeur spécifiée",
|
||||
],
|
||||
["-d", "--del-pkey", "args" => 1, "argsdesc" => "PKEY",
|
||||
"help" => "supprimer le chemin de clé spécifié",
|
||||
],
|
||||
["-r", "--filter-dep", "args" => 1, "argsdesc" => "DEP",
|
||||
"help" => "sélectionner tous les projets qui ont une dépendance sur la librairie spécifiée",
|
||||
],
|
||||
["-u", "--update-dep", "args" => "value", "argsdesc" => "VERSION",
|
||||
"help" => "mettre à jour la dépendance avec la nouvelle cible/version.
|
||||
requière l'utilisation de --filter-dep",
|
||||
],
|
||||
["--sn", "--show-names", "name" => "show", "value" => self::SHOW_NAMES,
|
||||
"help" => "afficher les noms des projets",
|
||||
],
|
||||
["--sp", "--show-deps", "name" => "show", "value" => self::SHOW_DEPS,
|
||||
"help" => "afficher les branches",
|
||||
],
|
||||
["--sb", "--show-branches",
|
||||
"help" => "avec --show-deps, afficher aussi les branches",
|
||||
],
|
||||
["--sf", "--show-filtered",
|
||||
"help" => "avec --show-deps, n'afficher que la dépendances sélectionnée
|
||||
implique --show-branches",
|
||||
],
|
||||
["--sd", "--show-dirs", "name" => "show", "value" => self::SHOW_DIRS,
|
||||
"help" => "afficher les répertoires des projets",
|
||||
],
|
||||
["--sc", "--show-composer", "name" => "show", "value" => self::SHOW_COMPOSER,
|
||||
"help" => "afficher les chemins des fichiers composer.json de chaque projet",
|
||||
],
|
||||
];
|
||||
|
||||
private $projectsDir;
|
||||
private $rewrite = false, $forceRewrite = false;
|
||||
private $setPkey, $delPkey;
|
||||
private $filterDep, $updateDep;
|
||||
private $show = self::SHOW_DEPS, $showBranches, $showFiltered;
|
||||
private $args;
|
||||
|
||||
function main() {
|
||||
$projectDirs = $this->args;
|
||||
$rewrite = $this->rewrite;
|
||||
$forceRewrite = $this->forceRewrite;
|
||||
$setPkey = $this->setPkey;
|
||||
$delPkey = $this->delPkey;
|
||||
$filterDep = $this->filterDep;
|
||||
$updateDep = $this->updateDep;
|
||||
$projectsDir = $this->projectsDir;
|
||||
$show = $this->show;
|
||||
$showBranches = $this->showBranches;
|
||||
$showFiltered = $this->showFiltered;
|
||||
|
||||
if ($updateDep && !$filterDep) {
|
||||
self::die("--update-dep requière --filter-dep");
|
||||
}
|
||||
|
||||
if (!$projectDirs) {
|
||||
if ($projectsDir !== null) {
|
||||
$projectDirs = shutils::ls_pdirs($projectsDir);
|
||||
} else {
|
||||
if (file_exists("composer.json")) {
|
||||
$projectDirs = ["."];
|
||||
} else {
|
||||
$projectsDir = ".";
|
||||
$projectDirs = shutils::ls_pdirs($projectsDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
$projects = ComposerWalker::build_projects($projectDirs, $projectsDir);
|
||||
$oprojects = ComposerWalker::order_deps(null, $projects);
|
||||
|
||||
foreach ($oprojects as $name) {
|
||||
$project = A::get($projects, $name);
|
||||
if ($project === null) continue;
|
||||
$requires = ComposerWalker::order_deps($project["requires"], $projects);
|
||||
$branches = $project["branches"];
|
||||
if ($filterDep && !in_array($filterDep, $requires)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($setPkey || $delPkey || $updateDep) {
|
||||
/** @var ComposerFile $composer */
|
||||
$composer = $project["composer"];
|
||||
msg::step($composer->getComposerFile());
|
||||
if ($setPkey) {
|
||||
[$pkey, $value] = self::split_pkey_value($setPkey);
|
||||
$composer->setPkey($pkey, $value, $rewrite, $forceRewrite);
|
||||
} elseif ($delPkey) {
|
||||
[$pkey, $value] = self::split_pkey_value($delPkey);
|
||||
$composer->delPkey($pkey, $rewrite, $forceRewrite);
|
||||
} elseif ($updateDep) {
|
||||
$composer->updateDep($filterDep, $updateDep, $rewrite, $forceRewrite);
|
||||
}
|
||||
} else {
|
||||
switch ($show) {
|
||||
case self::SHOW_NAMES:
|
||||
echo $name;
|
||||
break;
|
||||
case self::SHOW_DEPS:
|
||||
echo $name; $pos = strlen($name);
|
||||
$sep = ": ";
|
||||
foreach ($requires as $require) {
|
||||
if ($showFiltered && $require !== $filterDep) continue;
|
||||
$text = $require;
|
||||
if ($showBranches || $showFiltered) {
|
||||
$branch = A::get($branches, $require);
|
||||
if ($branch !== null) $text .= "[$branch]";
|
||||
}
|
||||
$size = strlen("$sep$text");
|
||||
if ($pos + $size > 80) {
|
||||
$sep = "\n".str_repeat(" ", 4); $pos = 4;
|
||||
$size = strlen($text);
|
||||
}
|
||||
echo "$sep$text"; $pos += $size;
|
||||
$sep = ", ";
|
||||
}
|
||||
break;
|
||||
case self::SHOW_DIRS:
|
||||
echo $project["dir"];
|
||||
break;
|
||||
case self::SHOW_COMPOSER:
|
||||
echo "$project[dir]/composer.json";
|
||||
break;
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ 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";
|
||||
$message = path::ppath($projdir).": aucune configuration n'a été trouvée";
|
||||
throw new ValueException($message);
|
||||
}
|
||||
$this->composerFile = $composerFile;
|
||||
|
@ -24,6 +24,10 @@ class ComposerFile {
|
|||
/** @var string */
|
||||
protected $composerFile;
|
||||
|
||||
function getComposerFile(): string {
|
||||
return $this->composerFile;
|
||||
}
|
||||
|
||||
/** @var array */
|
||||
protected $data;
|
||||
|
||||
|
@ -196,15 +200,36 @@ class ComposerFile {
|
|||
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);
|
||||
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 !== null) echo "$contents\n";
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
namespace nur\tools\compctl;
|
||||
|
||||
use Exception;
|
||||
use nur\A;
|
||||
|
||||
class ComposerWalker {
|
||||
protected static function get_project_dir($name, string $projects_dir) {
|
||||
$project_dir = A::get(ConfigFile::PROJNAMES, $name);
|
||||
if ($project_dir === null) {
|
||||
$project_dir = preg_replace('/\//', "-", $name, 1);
|
||||
}
|
||||
return "$projects_dir/$project_dir";
|
||||
}
|
||||
|
||||
static function parse_project(string $dir, array &$projects, ?array &$new_projects=null, bool $throw=false): ?array {
|
||||
try {
|
||||
$composer = new ComposerFile("$dir/composer.json");
|
||||
} catch (Exception $e) {
|
||||
if ($throw) throw $e;
|
||||
else return null;
|
||||
}
|
||||
|
||||
$pname = $composer->getv("name");
|
||||
if (array_key_exists($pname, $projects) && $projects[$pname] !== null) {
|
||||
# projet déjà analysé
|
||||
return $projects[$pname];
|
||||
}
|
||||
|
||||
$project = [
|
||||
"composer" => $composer,
|
||||
"name" => $pname,
|
||||
"requires" => [],
|
||||
"branches" => [],
|
||||
"dir" => realpath($dir),
|
||||
];
|
||||
$requires = $composer->geta("require");
|
||||
if ($requires !== null) {
|
||||
foreach ($requires as $dep => $version) {
|
||||
# ignorer les projets qui n'ont pas le préfixe requis
|
||||
$include_project = false;
|
||||
foreach (ConfigFile::PREFIXES as $prefix) {
|
||||
if (substr($dep, 0, strlen($prefix)) === $prefix) {
|
||||
$include_project = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$include_project) continue;
|
||||
# sinon, ajouter le projet aux dépendances
|
||||
$project["requires"][] = $dep;
|
||||
if (!array_key_exists($dep, $projects)) {
|
||||
$projects[$dep] = null;
|
||||
$new_projects[$dep] = true;
|
||||
}
|
||||
if (substr($version, 0, 4) === "dev-") {
|
||||
$project["branches"][$dep] = substr($version, 4);
|
||||
} else {
|
||||
$project["branches"][$dep] = $version;
|
||||
}
|
||||
}
|
||||
}
|
||||
$projects[$pname] = $project;
|
||||
return $project;
|
||||
}
|
||||
|
||||
static function build_projects(array $project_dirs, ?string &$projects_dir): array {
|
||||
$projects = [];
|
||||
foreach ($project_dirs as $project_dir) {
|
||||
if ($projects_dir === null) $projects_dir = dirname($project_dir);
|
||||
# analyser le projet initial
|
||||
$new_projects = [];
|
||||
if (self::parse_project($project_dir, $projects, $new_projects) === null) {
|
||||
continue;
|
||||
}
|
||||
# puis analyser récursivement tous les projets dépendants
|
||||
while (true) {
|
||||
$have_new = false;
|
||||
foreach ($new_projects as $name => &$new) {
|
||||
if ($new) {
|
||||
$new = false;
|
||||
$have_new = true;
|
||||
$project_dir = self::get_project_dir($name, $projects_dir);
|
||||
self::parse_project($project_dir, $projects, $new_projects);
|
||||
}
|
||||
}; unset($new);
|
||||
if (!$have_new) break;
|
||||
}
|
||||
}
|
||||
return $projects;
|
||||
}
|
||||
|
||||
static function order_deps(?array $deps, array $projects): array {
|
||||
if ($deps === null) $deps = array_keys($projects);
|
||||
$pi = 0;
|
||||
$max = count($deps);
|
||||
while ($pi < $max) {
|
||||
$modified = false;
|
||||
$project = $projects[$deps[$pi]];
|
||||
if ($project !== null) {
|
||||
# dépendance dont on a trouvé la correspondance sur disque
|
||||
foreach ($project["requires"] as $dep) {
|
||||
$di = array_search($dep, $deps);
|
||||
if ($di > $pi) {
|
||||
# si la dépendance se situe après le projet, la placer avant
|
||||
$deps = array_splice($deps, $di, 1);
|
||||
$deps = array_splice($deps, $pi, 0, [$dep]);
|
||||
$modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$modified) $pi++;
|
||||
}
|
||||
return $deps;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,9 @@ use nur\str;
|
|||
use nur\yaml;
|
||||
|
||||
class ConfigFile {
|
||||
# préfixes de projets dont on tient compte
|
||||
const PREFIXES = ["ur/", "nur/", "lib/", "nulib/"];
|
||||
|
||||
const DEFAULT_DEP = "dev-master";
|
||||
const DEFAULT_DEPS = [
|
||||
"wip" => "dev-wip",
|
||||
|
|
Loading…
Reference in New Issue