From a37d4f89dea2db696c05108e8d7bf89d55215a9c Mon Sep 17 00:00:00 2001 From: kj Date: Thu, 8 Oct 2026 08:34:54 -0300 Subject: [PATCH] fix(cli): adopt core records canonical hashes and flags local edits --- .../src/Toolbox/Commands/AdoptCommand.php | 33 ++++++++++++------- tests/group9_absorb_test.php | 28 ++++++++++++++++ 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/packages/commands/src/Toolbox/Commands/AdoptCommand.php b/packages/commands/src/Toolbox/Commands/AdoptCommand.php index 735cdd1..dc91fcb 100644 --- a/packages/commands/src/Toolbox/Commands/AdoptCommand.php +++ b/packages/commands/src/Toolbox/Commands/AdoptCommand.php @@ -89,7 +89,7 @@ final class AdoptCommand extends Command } } - $coreAdopted = $this->adoptCore($parsed['options'], $root, $lock, $dryRun); + [$coreAdopted, $coreHasLocal] = $this->adoptCore($parsed['options'], $root, $lock, $dryRun); if (!$dryRun) { $lock->save($root); @@ -100,7 +100,8 @@ final class AdoptCommand extends Command $this->console->ok('adoptado ' . $id . $mark); } if ($coreAdopted > 0) { - $this->console->ok("adoptado el core ({$coreAdopted} archivos)"); + $mark = $coreHasLocal ? ' (con ediciones locales)' : ''; + $this->console->ok("adoptado el core ({$coreAdopted} archivos){$mark}"); } if ($adopted === [] && $coreAdopted === 0) { $this->console->out('No se encontró nada que adoptar.'); @@ -111,33 +112,42 @@ final class AdoptCommand extends Command /** * Registra el core existente: sólo los archivos que también existen en el - * core de referencia, con su hash actual. No toca archivos. + * core de referencia, con su hash CANÓNICO. Si la copia local difiere, + * el archivo queda marcado como edición local y `update core` preguntará + * antes de pisarla (mismo contrato que `adoptComponent`). * * @param array $options + * @return array{0:int,1:bool} */ - private function adoptCore(array $options, string $root, $lock, bool $dryRun): int + private function adoptCore(array $options, string $root, $lock, bool $dryRun): array { if ($lock->core() !== null) { - return 0; + return [0, false]; } try { $provider = $this->coreProvider($options); } catch (ToolboxException $e) { $this->console->warn('no se pudo resolver el core para adoptar: ' . $e->getMessage()); - return 0; + return [0, false]; } - $files = []; + $files = []; + $hasLocal = false; foreach ($provider->files() as $relative) { $absolute = $root . '/' . $relative; - if (is_file($absolute)) { - $files[$relative] = Drift::hash($absolute); + if (!is_file($absolute)) { + continue; + } + $canonical = $provider->read($relative); + $files[$relative] = 'sha256:' . hash('sha256', $canonical); + if (file_get_contents($absolute) !== $canonical) { + $hasLocal = true; } } if ($files === []) { - return 0; + return [0, false]; } if (!$dryRun) { @@ -145,9 +155,10 @@ final class AdoptCommand extends Command 'version' => $provider->version(), 'commit' => $provider->commit(), 'files' => $files, + 'origin' => $hasLocal ? 'local' : 'adopted', ]); } - return count($files); + return [count($files), $hasLocal]; } } diff --git a/tests/group9_absorb_test.php b/tests/group9_absorb_test.php index 690867c..ddc4656 100644 --- a/tests/group9_absorb_test.php +++ b/tests/group9_absorb_test.php @@ -121,3 +121,31 @@ Harness::it('adopt registra el core existente con sus hashes', function () { rrmdir($proj); rrmdir($core); }); + +Harness::it('adopt del core registra hash canónico y marca ediciones locales', function () { + $repo = $GLOBALS['g9_repo']; + $core = g7_core('0.1.0'); + $proj = tmpdir('proj'); + + // Copia idéntica de Router.php + config.php customizado (proyecto heredado). + write_file($proj . '/src/Libs/Router.php', (string) file_get_contents($core . '/src/Libs/Router.php')); + write_file($proj . '/config.php', "run(['--root=' . $proj, '--toolbox=' . $repo, '--core-dir=' . $core]); + + $lock = Lock::load($proj); + $coreHashRouter = $lock->core()['files']['src/Libs/Router.php']; + $coreHashConfig = $lock->core()['files']['config.php']; + + $canonicalRouter = (string) file_get_contents($core . '/src/Libs/Router.php'); + $canonicalConfig = (string) file_get_contents($core . '/config.php'); + + eq('sha256:' . hash('sha256', $canonicalRouter), $coreHashRouter, 'hash de Router.php = canónico'); + eq('sha256:' . hash('sha256', $canonicalConfig), $coreHashConfig, 'hash de config.php = canónico (aunque el local difiera)'); + eq('local', $lock->core()['origin'] ?? null, 'origin = local porque hay ediciones locales'); + + ok(file_get_contents($proj . '/config.php') !== $canonicalConfig, 'la copia local customizada no se tocó'); + + rrmdir($proj); + rrmdir($core); +});