netgescon-day0/app/Console/Commands/NetgesconDevelopmentSnapshotCommand.php

295 lines
10 KiB
PHP

<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Process;
class NetgesconDevelopmentSnapshotCommand extends Command
{
protected $signature = 'netgescon:development-snapshot
{--output= : Output markdown custom}
{--json= : Output JSON custom}';
protected $description = 'Rigenera una snapshot locale di continuita sviluppo per Supporto > Modifiche';
public function handle(): int
{
$generatedDir = storage_path('app/support/generated');
File::ensureDirectoryExists($generatedDir);
$outputPath = trim((string) $this->option('output')) !== ''
? trim((string) $this->option('output'))
: $generatedDir . '/CONTINUITA-SVILUPPO-SNAPSHOT.md';
$jsonPath = trim((string) $this->option('json')) !== ''
? trim((string) $this->option('json'))
: $generatedDir . '/CONTINUITA-SVILUPPO-SNAPSHOT.json';
$manualPath = base_path('docs/support/MANUALE-SVILUPPO-OPERATIVO.md');
$bugPath = base_path('docs/support/BUG-RISOLTI-E-VALIDAZIONI.md');
$continuityPath = base_path('docs/support/CONTINUITA-SVILUPPO-AGENT.md');
$pbxPath = base_path('docs/support/PBX-PANASONIC-STATO-OPERATIVO.md');
$manual = is_file($manualPath) ? (string) @file_get_contents($manualPath) : '';
$bugs = is_file($bugPath) ? (string) @file_get_contents($bugPath) : '';
$continuity = is_file($continuityPath) ? (string) @file_get_contents($continuityPath) : '';
$pbx = is_file($pbxPath) ? (string) @file_get_contents($pbxPath) : '';
$branch = $this->gitValue(['rev-parse', '--abbrev-ref', 'HEAD']) ?? '-';
$commit = $this->gitValue(['rev-parse', '--short', 'HEAD']) ?? '-';
$recentCommits = $this->recentCommits();
$recentPages = $this->recentPages();
$commitNotes = $this->recentCommitNotes();
$snapshot = [];
$snapshot[] = '# Snapshot continuita sviluppo';
$snapshot[] = '';
$snapshot[] = 'Generata automaticamente il ' . now()->format('d/m/Y H:i:s') . '.';
$snapshot[] = '';
$snapshot[] = '## Stato Git corrente';
$snapshot[] = '';
$snapshot[] = '- Branch: `' . $branch . '`';
$snapshot[] = '- Commit: `' . $commit . '`';
$snapshot[] = '- Workspace: `' . base_path() . '`';
$snapshot[] = '';
$snapshot[] = '## Focus operativo';
$snapshot[] = '';
$snapshot[] = $this->normalizeSectionLines($this->extractSection($manual, '## Implementazioni recenti da tenere come riferimento'));
$snapshot[] = '';
$snapshot[] = '## Stato PBX Panasonic';
$snapshot[] = '';
$snapshot[] = $this->normalizeSectionLines($this->extractSection($pbx, '## Stato attuale'));
$snapshot[] = '';
$snapshot[] = '## Validazioni ancora aperte';
$snapshot[] = '';
$snapshot[] = $this->normalizeSectionLines($this->extractSection($bugs, '## Validazioni ancora aperte'));
$snapshot[] = '';
$snapshot[] = '## Prossimi passi consigliati';
$snapshot[] = '';
$snapshot[] = $this->normalizeSectionLines($this->extractSection($manual, '## Prossimi passi consigliati'));
$snapshot[] = '';
$snapshot[] = '## Regole di continuita';
$snapshot[] = '';
$snapshot[] = $this->normalizeSectionLines($this->extractSection($continuity, '## Regola semplice'));
$snapshot[] = '';
$snapshot[] = '## Ultimi commit';
$snapshot[] = '';
$snapshot[] = '| Data | Commit | Messaggio |';
$snapshot[] = '| --- | --- | --- |';
foreach ($recentCommits as $row) {
$snapshot[] = '| ' . $row['date'] . ' | `' . $row['hash'] . '` | ' . str_replace('|', '-', $row['message']) . ' |';
}
$snapshot[] = '';
$snapshot[] = '## Note commit recenti';
$snapshot[] = '';
if ($commitNotes === []) {
$snapshot[] = '- Nessuna nota commit strutturata disponibile tra gli ultimi commit.';
} else {
foreach ($commitNotes as $row) {
$snapshot[] = '- `' . $row['hash'] . '` ' . $row['message'];
$snapshot[] = ' - Nota: ' . $row['note'];
}
}
$snapshot[] = '';
$snapshot[] = '## Pagine toccate di recente';
$snapshot[] = '';
if ($recentPages === []) {
$snapshot[] = '- Nessuna pagina recente rilevata.';
} else {
foreach ($recentPages as $row) {
$snapshot[] = '- `' . $row['commit'] . '` ' . $row['page'] . ' -> `' . $row['path'] . '`';
}
}
$snapshot[] = '';
$snapshot[] = '## Fonti da leggere per ripartire';
$snapshot[] = '';
$snapshot[] = '- `docs/support/MANUALE-SVILUPPO-OPERATIVO.md`';
$snapshot[] = '- `docs/support/PBX-PANASONIC-STATO-OPERATIVO.md`';
$snapshot[] = '- `docs/support/BUG-RISOLTI-E-VALIDAZIONI.md`';
$snapshot[] = '- `docs/support/CONTINUITA-SVILUPPO-AGENT.md`';
File::put($outputPath, implode(PHP_EOL, $snapshot) . PHP_EOL);
File::put($jsonPath, json_encode([
'generated_at' => now()->toIso8601String(),
'branch' => $branch,
'commit' => $commit,
'output_path' => $outputPath,
'source_files' => [
'docs/support/MANUALE-SVILUPPO-OPERATIVO.md',
'docs/support/PBX-PANASONIC-STATO-OPERATIVO.md',
'docs/support/BUG-RISOLTI-E-VALIDAZIONI.md',
'docs/support/CONTINUITA-SVILUPPO-AGENT.md',
],
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
$this->info('Snapshot continuita sviluppo aggiornata.');
return self::SUCCESS;
}
private function gitValue(array $arguments): ?string
{
$result = Process::path(base_path())
->timeout(120)
->run(array_merge(['git'], $arguments));
if (! $result->successful()) {
return null;
}
$output = trim((string) $result->output());
return $output !== '' ? $output : null;
}
/** @return array<int,array{hash:string,date:string,message:string}> */
private function recentCommits(): array
{
$rows = [];
$result = Process::path(base_path())->timeout(120)->run([
'git', 'log', '-n', '8', '--date=format:%d/%m/%Y %H:%M', '--pretty=format:%h|%ad|%s',
]);
if (! $result->successful()) {
return [];
}
$lines = preg_split('/\r\n|\r|\n/', trim((string) $result->output())) ?: [];
foreach ($lines as $line) {
$parts = explode('|', $line, 3);
if (count($parts) !== 3) {
continue;
}
$rows[] = [
'hash' => (string) $parts[0],
'date' => (string) $parts[1],
'message' => (string) $parts[2],
];
}
return $rows;
}
/** @return array<int,array{page:string,path:string,commit:string}> */
private function recentPages(): array
{
$rows = [];
$seen = [];
$result = Process::path(base_path())->timeout(120)->run([
'git', 'log', '-n', '12', '--name-only', '--pretty=format:@@@%h', '--', 'app/Filament/Pages', 'resources/views/filament/pages',
]);
if (! $result->successful()) {
return [];
}
$currentCommit = null;
$lines = preg_split('/\r\n|\r|\n/', (string) $result->output()) ?: [];
foreach ($lines as $line) {
$line = trim((string) $line);
if ($line === '') {
continue;
}
if (str_starts_with($line, '@@@')) {
$currentCommit = substr($line, 3);
continue;
}
if ($currentCommit === null || isset($seen[$line])) {
continue;
}
if (! str_starts_with($line, 'app/Filament/Pages/') && ! str_starts_with($line, 'resources/views/filament/pages/')) {
continue;
}
$seen[$line] = true;
$rows[] = [
'page' => basename($line),
'path' => $line,
'commit' => $currentCommit,
];
if (count($rows) >= 10) {
break;
}
}
return $rows;
}
/** @return array<int,array{hash:string,message:string,note:string}> */
private function recentCommitNotes(): array
{
$path = storage_path('app/support/commit-notes.json');
if (! is_file($path)) {
return [];
}
$decoded = json_decode((string) @file_get_contents($path), true);
if (! is_array($decoded)) {
return [];
}
$rows = [];
foreach ($this->recentCommits() as $commit) {
$fullHash = $this->gitValue(['rev-parse', $commit['hash']]);
if (! is_string($fullHash) || ! isset($decoded[$fullHash]) || ! is_string($decoded[$fullHash])) {
continue;
}
$rows[] = [
'hash' => $commit['hash'],
'message' => $commit['message'],
'note' => trim($decoded[$fullHash]),
];
}
return array_slice($rows, 0, 5);
}
private function extractSection(string $markdown, string $heading): string
{
if ($markdown === '' || ! str_contains($markdown, $heading)) {
return '- Sezione non disponibile.';
}
$offset = strpos($markdown, $heading);
if ($offset === false) {
return '- Sezione non disponibile.';
}
$slice = substr($markdown, $offset + strlen($heading));
if (! is_string($slice)) {
return '- Sezione non disponibile.';
}
if (preg_match('/\n##\s+/', $slice, $match, PREG_OFFSET_CAPTURE) === 1) {
$slice = substr($slice, 0, (int) $match[0][1]);
}
return trim($slice) !== '' ? trim($slice) : '- Sezione non disponibile.';
}
private function normalizeSectionLines(string $text): string
{
$lines = preg_split('/\r\n|\r|\n/', trim($text)) ?: [];
$cleaned = [];
foreach ($lines as $line) {
$line = trim((string) $line);
if ($line === '') {
continue;
}
$cleaned[] = $line;
}
return $cleaned === [] ? '- Nessuna informazione disponibile.' : implode(PHP_EOL, $cleaned);
}
}