Browse Source

On sait générer toutes les documentations depuis un fichier de type autodoc.txt

master
Matthieu Marcillaud 9 months ago
parent
commit
7f9e3dd8ef
  1. 53
      old/FromFile.php
  2. 2
      placeholders/phpdoc.xml
  3. 2
      src/Application.php
  4. 110
      src/AutodocFile.php
  5. 2
      src/Command/FromDirectory.php
  6. 116
      src/Command/FromFile.php
  7. 2
      src/Command/FromGit.php
  8. 2
      src/Command/FromPlugin.php
  9. 2
      src/Command/FromSpip.php
  10. 2
      src/Command/FromZone.php
  11. 12
      src/Stage/PhpDocumentorConfigStage.php
  12. 2
      src/Stage/PhpDocumentorStage.php

53
old/FromFile.php

@ -1,53 +0,0 @@
<?php
/*
* Commande d'exécution depuis un fichier autodoc.txt (de la Zone de SPIP par défaut)…
*/
namespace Spip\Autodoc\Command;
use Spip\Autodoc\Generator;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Command\Command;
/**
* Déclaration et exécution de l'application depuis un fichier de type autodoc.txt
*/
class FromFile extends Command
{
protected function configure()
{
$this
->setName('from:file')
->setDescription("Exécuter l'autodoc sur un fichier de type autodoc.txt.")
->setHelp("Le fichier source des données doit indiquer une liste de sources GIT et leur préfixes de stockage.
Pour chaque ligne, une documentation sera générée. Lorsque le contenu est un plugin de SPIP, certaines informations seront extraites du paquet.xml et du fichier de langue.
Exemple de fichier :
https://git.spip.net/spip-contrib-extensions/a2a.git@master;a2a
https://git.spip.net/spip-contrib-extensions/champs_extras_core@master;cextras
")
->addArgument('file', InputArgument::OPTIONAL, "Chemin du fichier brut.", 'https://git.spip.net/spip-contrib-outils/archivelists/raw/branch/master/autodoc.txt')
->addOption('sorties', 's', InputOption::VALUE_OPTIONAL, "Répertoire stockant toutes les documentations générées. Chemin absolu ou relatif au répertoire 'work'. <comment>Défaut : 'output'</comment>")
->addOption('topnav', null, InputOption::VALUE_OPTIONAL, "URL d'un fichier JS à charger dans le head html. <comment>Exemple : '//boussole.spip.net/?page=spipnav.js&lang=fr'</comment>")
->addOption('avec_boussole_spip', null, InputOption::VALUE_NONE, "Intègre le JS de la boussole SPIP en entête topnav.")
->addOption('prefixe', 'p', InputOption::VALUE_OPTIONAL, "Préfixe de plugin. Lui seul sera actualisé si présent.")
->addOption('force', '', InputOption::VALUE_NONE, "Force la génération de la documentation même si le plugin n'a eu de modifications depuis la fois précédente.")
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$file = $input->getArgument('file');
$output->writeln("\nExécuter autodoc sur un fichier : <info>$file</info>.");
$generator = new Generator($input, $output);
$generator->generateFromFile($file);
}
}

2
placeholders/phpdoc.xml

@ -29,7 +29,7 @@
<path>squelettes-dist/**/*</path>
</ignore>
<!-- default-package-name>@DEFAULT_PACKAGE_NAME@</default-package-name -->
<include-source>true</include-source>
<!-- include-source>true</include-source -->
<markers>
<marker>TODO</marker>
<marker>FIXME</marker>

2
src/Application.php

@ -22,6 +22,6 @@ class Application extends ConsoleApplication
$this->add(new Command\FromZone());
$this->add(new Command\FromGit());
$this->add(new Command\FromDirectory());
#$this->add(new Command\FromFile());
$this->add(new Command\FromFile());
}
}

110
src/AutodocFile.php

@ -0,0 +1,110 @@
<?php
namespace Spip\Autodoc;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Spip\Autodoc\Exception\AutodocException;
use Symfony\Component\Filesystem\Filesystem;
class AutodocFile {
private array $items = [];
private string $file;
private string $old_file;
public function __construct(
private string $source,
private string $cache_directory,
private ?LoggerInterface $logger = null,
private ?string $only_one_prefix = null,
) {
if ($logger === null) {
$this->logger = new NullLogger();
}
$this->file = $cache_directory . '/autodoc.txt';
$this->old_file = $cache_directory . '/autodoc.old.txt';
$this->analyse();
}
/** @return array<string, Git> */
public function getItems(): array {
if ($this->only_one_prefix) {
return array_intersect_key($this->items, [$this->only_one_prefix => true]);
}
return $this->items;
}
private function analyse() {
$this->rotate();
$this->items = $this->parse($this->file);
}
private function rotate() {
if (!$this->source) {
throw new AutodocException('No source for autodoc file');
}
$content = file_get_contents($this->source);
if (!$content) {
throw new AutodocException('Can’t fetch autodoc file, or file is empty');
}
$fs = new Filesystem();
if (!$fs->exists($this->cache_directory)) {
$fs->mkdir($$this->cache_directory);
}
$fs->remove($this->old_file);
if ($fs->exists($this->file)) {
$fs->copy($this->file, $this->old_file);
}
file_put_contents($this->file, $content);
}
private function parse(string $file, bool $write_errors = true): array
{
if (!$lines = file($file)) {
throw new AutodocException("Can’t read autodoc file");
}
$list = [];
foreach ($lines as $lineno => $line) {
if (!$line) {
continue;
}
$line = trim($line);
if (!$line or $line[0] === '#') {
continue;
}
$couples = explode(';', $line);
$lineno++;
if (count($couples) !== 2) {
if (count($couples) === 1) {
$this->logger->error(sprintf('Line %s ignored. No prefix on content "%s"', $lineno, $line));
} else {
$this->logger->error(sprintf('Line %s ignored. Too much parts on content "%s"', $lineno, $line));
}
continue;
}
list($url, $prefix) = $couples;
if (!$url) {
$this->logger->error(sprintf('Line %s ignored. URL unknown on content "%s"', $lineno, $line));
continue;
}
if (!$prefix) {
$this->logger->error(sprintf('Line %s ignored. No prefix on content "%s"', $lineno, $line));
continue;
}
if (isset($list[$prefix])) {
$this->logger->error(sprintf('Line %s ignored. Prefix "%s" already present on content "%s"', $lineno, $prefix, $line));
continue;
}
list($url, $branch) = array_pad(explode('@', $url), 2, null);
$branch = $branch ?: 'master';
// pas d'erreur !
$list[$prefix] = (new Git($url))->setBranch($branch);
}
return $list;
}
}

2
src/Command/FromDirectory.php

@ -33,7 +33,7 @@ class FromDirectory extends Command
->setHelp('Si le répertoire cible est un plugin SPIP, certaines informations seront extraites du paquet.xml et du fichier de langue.')
->addArgument('source', InputArgument::REQUIRED, 'Chemin du répertoire. <comment>Exemple : "/home/user/www/spip/plugins/fabrique"</comment>')
->addOption('prefix', 'p', InputOption::VALUE_OPTIONAL, 'Préfixe servant au stockage des données si pas de paquet.xml <comment>Défaut: "default"</comment>')
->addOption('output', 'o', InputOption::VALUE_OPTIONAL, 'Répertoire recevant le HTML généré. Chemin absolu ou relatif au répertoire "work". <comment>Défaut: "var/output/{prefixe}"</comment>')
->addOption('output', 'o', InputOption::VALUE_OPTIONAL, 'Répertoire recevant le HTML généré. Chemin absolu ou relatif au répertoire courant. <comment>Défaut: "var/output/{prefixe}"</comment>')
->addOption('topnav', null, InputOption::VALUE_OPTIONAL, 'URL d’un fichier JS à charger dans le head html. <comment>Exemple: "//boussole.spip.net/?page=spipnav.js&lang=fr"</comment>')
->addOption('topnav_spip', null, InputOption::VALUE_NONE, 'Intègre le JS de la boussole SPIP en entête topnav.')
->addOption('force', '', InputOption::VALUE_NONE, 'Force l’analyse de tous les fichiers, même s’ils n’ont pas été modifiés.');

116
src/Command/FromFile.php

@ -0,0 +1,116 @@
<?php
/*
* Commande d'exécution depuis un fichier autodoc.txt (de la Zone de SPIP par défaut)…
*/
namespace Spip\Autodoc\Command;
use League\Pipeline\InterruptibleProcessor;
use League\Pipeline\Pipeline;
use Spip\Autodoc\AutodocFile;
use Spip\Autodoc\Context;
use Spip\Autodoc\Exception\AutodocException;
use Spip\Autodoc\Git;
use Spip\Autodoc\Stage\CheckStage;
use Spip\Autodoc\Stage\GitStage;
use Spip\Autodoc\Stage\JsonAutodocStage;
use Spip\Autodoc\Stage\PackageStage;
use Spip\Autodoc\Stage\PhpDocumentorConfigStage;
use Spip\Autodoc\Stage\PhpDocumentorStage;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Déclaration et exécution de l'application depuis un fichier de type autodoc.txt
*/
class FromFile extends Command
{
protected function configure()
{
$this
->setName('from:file')
->setDescription('Exécuter l’autodoc sur un fichier de type autodoc.txt')
->setHelp('Le fichier source des données doit indiquer une liste de sources GIT et leur préfixes de stockage.
Pour chaque ligne, une documentation sera générée. Lorsque le contenu est un plugin de SPIP, certaines informations seront extraites du paquet.xml et du fichier de langue.
Exemple de fichier :
https://git.spip.net/spip-contrib-extensions/a2a.git@master;a2a
https://git.spip.net/spip-contrib-extensions/champs_extras_core@master;cextras
')
->addArgument('source', InputArgument::OPTIONAL, 'Chemin ou url du fichier', 'https://git.spip.net/spip-contrib-outils/archivelists/raw/branch/master/autodoc.txt')
->addOption('outputs', 'o', InputOption::VALUE_OPTIONAL, 'Répertoire stockant toutes les documentations générées. Chemin absolu ou relatif au répertoire courant. <comment>Défaut : "var/output"</comment>')
->addOption('prefix', 'p', InputOption::VALUE_OPTIONAL, "Préfixe de plugin. Lui seul sera actualisé si présent.")
->addOption('topnav', null, InputOption::VALUE_OPTIONAL, 'URL d’un fichier JS à charger dans le head html. <comment>Exemple: "//boussole.spip.net/?page=spipnav.js&lang=fr"</comment>')
->addOption('topnav_spip', null, InputOption::VALUE_NONE, 'Intègre le JS de la boussole SPIP en entête topnav.')
->addOption('force', '', InputOption::VALUE_NONE, 'Force l’analyse de tous les fichiers, même s’ils n’ont pas été modifiés.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$context = new Context($input, $output);
/** @var SymfonyStyle */
$io = $context->get('io');
$io->title("Autodoc " . $this->getName());
if ($input->hasArgument('source')) {
$source = $input->getArgument('source');
$io->text("Source: <info>$source</info>.");
}
if (!$source) {
throw new AutodocException("Source must be provide");
}
if ($input->getOption('outputs')) {
$context->set('output_base_directory', $input->getOption('outputs'));
}
if ($input->getOption('prefix')) {
$context->set('only_this_prefix', $input->getOption('outputs'));
}
$context->set('topnav', $input->getOption('topnav'));
$context->set('topnav_spip', $input->getOption('topnav_spip'));
$context->set('phpdocumentor_force', $input->getOption('force'));
$autodoc = new AutodocFile(
source: $source,
cache_directory: $context->get('input_base_directory'),
logger: $context->get('logger'),
only_one_prefix: $input->getOption('prefix') ?: null,
);
$items = $autodoc->getItems();
$io->comment(sprintf('%s projets à documenter', count($items)));
foreach ($items as $prefix => $git) {
$io->section($prefix);
$item = clone $context;
$item->set('default_prefix', $prefix);
$item->set('git_asked', $git);
$this->pipeline($item);
}
return Command::SUCCESS;
}
protected function pipeline(Context $context): void
{
$processor = new InterruptibleProcessor(fn (Context $context) => $context->empty('errors'));
$pipeline = (new Pipeline($processor))
->pipe(new CheckStage())
->pipe(new GitStage())
->pipe(new PackageStage())
->pipe(new PhpDocumentorConfigStage())
->pipe(new PhpDocumentorStage())
->pipe(new JsonAutodocStage());
$pipeline->process($context);
}
}

2
src/Command/FromGit.php

@ -38,7 +38,7 @@ class FromGit extends Command
->addArgument('source', InputArgument::REQUIRED, 'URL de la source GIT. <comment>Exemple : "https://git.spip.net/spip/spip.git"</comment>')
->addOption('branch', 'b', InputOption::VALUE_OPTIONAL, 'Branche ou tag à utiliser', 'master')
->addOption('prefix', 'p', InputOption::VALUE_OPTIONAL, 'Préfixe servant au stockage des données')
->addOption('output', 'o', InputOption::VALUE_OPTIONAL, 'Répertoire recevant le HTML généré. Chemin absolu ou relatif au répertoire "work". <comment>Défaut: "var/output/{prefixe}"</comment>')
->addOption('output', 'o', InputOption::VALUE_OPTIONAL, 'Répertoire recevant le HTML généré. Chemin absolu ou relatif au répertoire courant. <comment>Défaut: "var/output/{prefixe}"</comment>')
->addOption('topnav', null, InputOption::VALUE_OPTIONAL, 'URL d’un fichier JS à charger dans le head html. <comment>Exemple: "//boussole.spip.net/?page=spipnav.js&lang=fr"</comment>')
->addOption('topnav_spip', null, InputOption::VALUE_NONE, 'Intègre le JS de la boussole SPIP en entête topnav.')
->addOption('force', '', InputOption::VALUE_NONE, 'Force l’analyse de tous les fichiers, même s’ils n’ont pas été modifiés.');

2
src/Command/FromPlugin.php

@ -23,7 +23,7 @@ class FromPlugin extends FromGit
->addArgument('source', InputArgument::REQUIRED, 'Chemin depuis spip-contrib-extensions du Git de la zone. <comment>Exemple: "fabrique"</comment>')
->addOption('branch', 'b', InputOption::VALUE_OPTIONAL, 'Branche ou tag à utiliser', 'master')
->addOption('prefix', 'p', InputOption::VALUE_OPTIONAL, 'Préfixe servant au stockage des données si pas de paquet.xml <comment>Défaut: "default"</comment>')
->addOption('output', 'o', InputOption::VALUE_OPTIONAL, 'Répertoire recevant le HTML généré. Chemin absolu ou relatif au répertoire "work". <comment>Défaut: "var/output/{prefixe}"</comment>')
->addOption('output', 'o', InputOption::VALUE_OPTIONAL, 'Répertoire recevant le HTML généré. Chemin absolu ou relatif au répertoire courant. <comment>Défaut: "var/output/{prefixe}"</comment>')
->addOption('topnav', null, InputOption::VALUE_OPTIONAL, 'URL d’un fichier JS à charger dans le head html. <comment>Exemple: "//boussole.spip.net/?page=spipnav.js&lang=fr"</comment>')
->addOption('topnav_spip', null, InputOption::VALUE_NONE, 'Intègre le JS de la boussole SPIP en entête topnav.')
->addOption('force', '', InputOption::VALUE_NONE, 'Force l’analyse de tous les fichiers, même s’ils n’ont pas été modifiés.');

2
src/Command/FromSpip.php

@ -30,7 +30,7 @@ class FromSpip extends FromGit
->addOption('branch', 'b', InputOption::VALUE_OPTIONAL, 'Branche ou tag à utiliser', 'master')
->addOption('prefix', 'p', InputOption::VALUE_OPTIONAL, 'Préfixe servant au stockage des données si pas de paquet.xml <comment>Défaut: "default"</comment>')
->addOption('output', 'o', InputOption::VALUE_OPTIONAL, 'Répertoire recevant le HTML généré. Chemin absolu ou relatif au répertoire "work". <comment>Défaut: "var/output/{prefixe}"</comment>')
->addOption('output', 'o', InputOption::VALUE_OPTIONAL, 'Répertoire recevant le HTML généré. Chemin absolu ou relatif au répertoire courant. <comment>Défaut: "var/output/{prefixe}"</comment>')
->addOption('topnav', null, InputOption::VALUE_OPTIONAL, 'URL d’un fichier JS à charger dans le head html. <comment>Exemple: "//boussole.spip.net/?page=spipnav.js&lang=fr"</comment>')
->addOption('topnav_spip', null, InputOption::VALUE_NONE, 'Intègre le JS de la boussole SPIP en entête topnav.')
->addOption('force', '', InputOption::VALUE_NONE, 'Force l’analyse de tous les fichiers, même s’ils n’ont pas été modifiés.')

2
src/Command/FromZone.php

@ -23,7 +23,7 @@ class FromZone extends FromGit
->addArgument('source', InputArgument::REQUIRED, 'Chemin dans l’arborescence du git de la zone. <comment>Exemple: "spip-contrib-extensions/fabrique"</comment>')
->addOption('branch', 'b', InputOption::VALUE_OPTIONAL, 'Branche ou tag à utiliser', 'master')
->addOption('prefix', 'p', InputOption::VALUE_OPTIONAL, 'Préfixe servant au stockage des données si pas de paquet.xml <comment>Défaut: "default"</comment>')
->addOption('output', 'o', InputOption::VALUE_OPTIONAL, 'Répertoire recevant le HTML généré. Chemin absolu ou relatif au répertoire "work". <comment>Défaut: "var/output/{prefixe}"</comment>')
->addOption('output', 'o', InputOption::VALUE_OPTIONAL, 'Répertoire recevant le HTML généré. Chemin absolu ou relatif au répertoire courant. <comment>Défaut: "var/output/{prefixe}"</comment>')
->addOption('topnav', null, InputOption::VALUE_OPTIONAL, 'URL d’un fichier JS à charger dans le head html. <comment>Exemple: "//boussole.spip.net/?page=spipnav.js&lang=fr"</comment>')
->addOption('topnav_spip', null, InputOption::VALUE_NONE, 'Intègre le JS de la boussole SPIP en entête topnav.')
->addOption('force', '', InputOption::VALUE_NONE, 'Force l’analyse de tous les fichiers, même s’ils n’ont pas été modifiés.');

12
src/Stage/PhpDocumentorConfigStage.php

@ -77,10 +77,10 @@ class PhpDocumentorConfigStage implements StageInterface
'@PREFIX@' => $prefix,
'@DEFAULT_PACKAGE_NAME@' => $context->get('default_prefix'),
'@TITLE@' => $context->get('title'),
'@SLOGAN@' => $package->get('slogan'),
'@DESCRIPTION@' => $package->get('description'),
'@PRESENTATION@' => $context->get('presentation'),
'@TITLE@' => $this->escape_xml($context->get('title')),
'@SLOGAN@' => $this->escape_xml($package->get('slogan')),
'@DESCRIPTION@' => $this->escape_xml($package->get('description')),
'@PRESENTATION@' => $this->escape_xml($context->get('presentation')),
'@VERSION@' => $package->get('version'),
'@REVISION@' => ($context->has('git') ? $context->get('git')->getCommit() : ''),
'@URL_PACKAGE@' => $package->get('url_package'),
@ -97,4 +97,8 @@ class PhpDocumentorConfigStage implements StageInterface
return $context;
}
private function escape_xml(string $content) {
return htmlspecialchars($content, \ENT_XML1, 'UTF-8');
}
}

2
src/Stage/PhpDocumentorStage.php

@ -19,6 +19,8 @@ class PhpDocumentorStage implements StageInterface
if ($context->has('phpdocumentor_force') and $context->get('phpdocumentor_force')) {
$cmd .= ' --force';
}
// --sourcecode
/** @var SymfonyStyle */
$io = $context->get('io');
$cmd .= match (true) {

Loading…
Cancel
Save