diff --git a/src/SonsOfPHP/Bard/src/Console/Command/PushCommand.php b/src/SonsOfPHP/Bard/src/Console/Command/PushCommand.php index bafb0026..98597fcb 100644 --- a/src/SonsOfPHP/Bard/src/Console/Command/PushCommand.php +++ b/src/SonsOfPHP/Bard/src/Console/Command/PushCommand.php @@ -50,7 +50,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int ['git', 'subtree', 'push', '-P', $pkg['path'], $pkg['repository'], $input->getOption('branch')], ]; - $io->text(sprintf('Pushing %s', $pkgName)); + $output->writeln($this->getHelper('formatter')->formatSection($pkgName, 'Pushing...')); foreach ($commands as $cmd) { $process = new Process($cmd); $io->text($process->getCommandLine()); @@ -60,6 +60,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int ->wait(); } } + + $output->writeln($this->getHelper('formatter')->formatSection($pkgName, '...Done')); + $io->newLine(); } $io->success('All Packages have been published.'); diff --git a/src/SonsOfPHP/Bard/src/Console/Command/ReleaseCommand.php b/src/SonsOfPHP/Bard/src/Console/Command/ReleaseCommand.php index aebc36ae..190d40bc 100644 --- a/src/SonsOfPHP/Bard/src/Console/Command/ReleaseCommand.php +++ b/src/SonsOfPHP/Bard/src/Console/Command/ReleaseCommand.php @@ -7,9 +7,11 @@ use RuntimeException; use SonsOfPHP\Bard\JsonFile; use SonsOfPHP\Bard\Worker\File\Bard\UpdateVersion; +use SonsOfPHP\Bard\Worker\File\Composer\Package\BranchAlias; use SonsOfPHP\Bard\Worker\File\Composer\Root\UpdateReplaceSection; use SonsOfPHP\Component\Version\Version; use SonsOfPHP\Component\Version\VersionInterface; +use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -26,10 +28,14 @@ final class ReleaseCommand extends AbstractCommand { private JsonFile $bardConfig; - private VersionInterface|Version|null $releaseVersion = null; + private VersionInterface|null $releaseVersion = null; private bool $isDryRun = true; + private JsonFile $rootComposerJsonFile; + + private SymfonyStyle $io; + protected function configure(): void { $this @@ -40,14 +46,14 @@ protected function configure(): void ->addArgument('release', InputArgument::REQUIRED, 'Next Release you want to start? Use format ..-+ or "major", "minor", "patch"') ->setHelp( <<<'EOT' - This command allows you to create a new release and will update the various - repos that have been configured. The current version can be found in the - `bard.json` file. This will will update the version based on the type of release - that you are doing. + This command allows you to create a new release and will update the various + repos that have been configured. The current version can be found in the + `bard.json` file. This will will update the version based on the type of release + that you are doing. - %command.full_name% + %command.full_name% - Read more at https://docs.SonsOfPHP.com + Read more at https://docs.SonsOfPHP.com EOT ); } @@ -83,62 +89,87 @@ protected function initialize(InputInterface $input, OutputInterface $output): v } $this->isDryRun = $input->getOption('dry-run'); + $this->io = new SymfonyStyle($input, $output); + + $this->rootComposerJsonFile = new JsonFile($input->getOption('working-dir') . '/composer.json'); } protected function execute(InputInterface $input, OutputInterface $output): int { - $formatter = $this->getHelper('formatter'); - $io = new SymfonyStyle($input, $output); if ($this->isDryRun) { - $io->note('dry-run enabled no changes will be made'); + $this->io->note('dry-run enabled no changes will be made'); } - $io->text([ - sprintf('Bumping release from %s to %s', $this->bardConfig->getSection('version'), $this->releaseVersion->toString()), - ]); + $this->io->text(sprintf('Bumping release from %s to %s', $this->bardConfig->getSection('version'), $this->releaseVersion->toString())); + + // 0. Make sure we have the latest changes + $this->pullLatestChanges($input, $output); + + // 1. Update "replace" in main composer.json with the Package Names + // "package/name": "self.version" + $this->updateReplace($input, $output); + + // @todo + // 2. Update Changelog + // Changes the "Unreleased" headline to the version we are releaseing + // Adds new headline at top for unreleased features + + // 3. Tag Release and push + $this->tagReleaseAndPushMonorepo($input, $output); + + // 4. Subtree Split for each package + $this->tagReleaseAndPushPackages($input, $output); + + // 5. Update branch alias in all composer.json files + $this->updateBranchAliasForPackages($input, $output); + + // 6. Update bard.json with current version + $this->updateBardConfigVersion(); - // Make sure we have the latest changes + // 7. Commit and push updates + $this->commitAndPushNewChanges($input, $output); + + $this->io->success('Congrations on your new release'); + if ($this->isDryRun) { + $this->io->note([ + 'dry-run was enabled so no files were changed and no code was pushed', + ]); + } + + return self::SUCCESS; + } + + private function pullLatestChanges(InputInterface $input, OutputInterface $output): void + { + $this->io->section('Making sure branch is up to date with latest changes'); $process = new Process(['git', 'pull', 'origin', $input->getOption('branch')]); - $io->text($process->getCommandLine()); + $this->io->text($process->getCommandLine()); if (!$this->isDryRun) { $this->getHelper('process')->mustRun($output, $process, sprintf('There was and error running command: %s', $process->getCommandLine())); } - $rootComposerJsonFile = new JsonFile($input->getOption('working-dir') . '/composer.json'); + $this->io->success('Done'); + } - // 1. Update "replace" in main composer.json with the Package Names - // "package/name": "self.version" - $io->section('updating root composer.json "replace" section with package information'); + private function updateReplace(InputInterface $input, OutputInterface $output): void + { + $this->io->section('updating root composer.json "replace" section with package information'); foreach ($this->bardConfig->getSection('packages') as $pkg) { $pkgComposerJsonFile = new JsonFile(realpath($input->getOption('working-dir') . '/' . $pkg['path'] . '/composer.json')); - $output->writeln([ - $formatter->formatSection($pkgComposerJsonFile->getSection('name'), 'Updating root composer.json'), - ]); - $rootComposerJsonFile = $rootComposerJsonFile->with(new UpdateReplaceSection($pkgComposerJsonFile)); + $output->writeln($this->getHelper('formatter')->formatSection($pkgComposerJsonFile->getSection('name'), 'Updating root composer.json')); + $this->rootComposerJsonFile = $this->rootComposerJsonFile->with(new UpdateReplaceSection($pkgComposerJsonFile)); } - $output->writeln([ - 'saving root composer.json', - ]); if (!$this->isDryRun) { - file_put_contents($rootComposerJsonFile->getFilename(), $rootComposerJsonFile->toJson()); + $this->rootComposerJsonFile->save(); } - $output->writeln([ - 'root composer.json file saved', - ]); - $io->newLine(); - $io->success('root "composer.json" update complete'); - - // @todo - // 2. Update Changelog - // Changes the "Unreleased" headline to the version we are releaseing - // Adds new headline at top for unreleased features - - // 3. Tag Release and push - $io->newLine(); - $io->section(sprintf('updating mother repo for release %s', $this->releaseVersion->toString())); + $this->io->success('Done'); + } + private function tagReleaseAndPushMonorepo(InputInterface $input, OutputInterface $output): void + { + $this->io->section(sprintf('updating mother repo for release %s', $this->releaseVersion->toString())); $processCommands = [ ['git', 'add', '.'], ['git', 'commit', '--allow-empty', '-m', sprintf('"Preparing for Release v%s"', $this->releaseVersion->toString())], @@ -148,22 +179,22 @@ protected function execute(InputInterface $input, OutputInterface $output): int ]; foreach ($processCommands as $cmd) { $process = new Process($cmd); - $io->text($process->getCommandLine()); + $this->io->text($process->getCommandLine()); if (!$this->isDryRun) { $this->getHelper('process')->mustRun($output, $process, sprintf('There was and error running command: %s', $process->getCommandLine())); } } - $io->success('Mother Repository Released'); + $this->io->success('Mother Repository Released'); + } - // 4. Subtree Split for each package - // @todo run split command - $io->newLine(); - $io->title(sprintf('updating package repos with release %s', $this->releaseVersion->toString())); + private function tagReleaseAndPushPackages(InputInterface $input, OutputInterface $output): void + { + $this->io->section(sprintf('updating package repos with release %s', $this->releaseVersion->toString())); foreach ($this->bardConfig->getSection('packages') as $pkg) { $pkgComposerJsonFile = new JsonFile(realpath($input->getOption('working-dir') . '/' . $pkg['path'] . '/composer.json')); $pkgName = $pkgComposerJsonFile->getSection('name'); - $io->text(sprintf('Package %s is being released', $pkgName)); + $output->writeln($this->getHelper('formatter')->formatSection($pkgName, 'Releasing...')); $processCommands = [ ['git', 'subtree', 'split', '-P', $pkg['path'], '-b', $pkgName], ['git', 'checkout', $pkgName], @@ -176,56 +207,83 @@ protected function execute(InputInterface $input, OutputInterface $output): int ]; foreach ($processCommands as $cmd) { $process = new Process($cmd); - $io->text($process->getCommandLine()); + $this->io->text($process->getCommandLine()); if (!$this->isDryRun) { $this->getHelper('process')->mustRun($output, $process, sprintf('There was and error running command: %s', $process->getCommandLine())); } } - $io->newLine(); + $output->writeln($this->getHelper('formatter')->formatSection($pkgName, '...Done')); + $this->io->newLine(); } - $io->text('All Packages have been Released'); - // $io->success('All Packages have been Released'); + $this->io->success('All Packages have been Released'); + } - // 5. Update branch alias in all composer.json files - // - Only if update is major or minor - // $io->section('Updating Branch Alias in root and packages'); + /** + * Foreach package, this will update the extra.branch-alias to the same + * value as the root composer.json value + */ + private function updateBranchAliasForPackages(InputInterface $input, OutputInterface $output): void + { + $this->io->section('Updating the Branch Alias for root and packages'); + $extra = $this->rootComposerJsonFile->getSection('extra'); + $branchAlias = explode('.', $this->releaseVersion->toString()); + $branchAlias[2] = 'x-dev'; + $branchAlias = implode('.', $branchAlias); + $extra['branch-alias'] = $branchAlias; + $this->rootComposerJsonFile->setSection('extra', $extra); + $this->io->text('root composer.json updated to ' . $branchAlias); + if (!$this->isDryRun) { + $this->rootComposerJsonFile->save(); + } - // 6. Update bard.json with current version - $io->section('Updating version in bard.json'); + foreach ($this->bardConfig->getSection('packages') as $pkg) { + $pkgComposerJsonFile = new JsonFile(realpath($input->getOption('working-dir') . '/' . $pkg['path'] . '/composer.json')); + $pkgComposerJsonFile = $pkgComposerJsonFile->with(new BranchAlias($this->rootComposerJsonFile)); + $output->writeln($this->getHelper('formatter')->formatSection($pkgComposerJsonFile->getSection('name'), 'Updated branch alias to "' . $branchAlias . '"')); + if (!$this->isDryRun) { + $pkgComposerJsonFile->save(); + } + } + $this->io->success('Done'); + } + + private function updateBardConfigVersion(): void + { + $this->io->section('Updating version in bard.json'); $this->bardConfig = $this->bardConfig->with(new UpdateVersion($this->releaseVersion)); if (!$this->isDryRun) { - file_put_contents($this->bardConfig->getFilename(), $this->bardConfig->toJson()); + $this->bardConfig->save(); } - $io->text('bard.json updated with new version'); + $this->io->success('bard.json updated with new version'); + } - // 7. Commit and push updates - // $io->section('Updating mother repo'); - // $processCommands = [ - // ['git', 'add', '.'], - // ['git', 'commit', '-m', '"starting release"'], - // ['git', 'push', 'origin', $input->getOption('branch')], - // ]; - // foreach ($processCommands as $cmd) { - // $process = new Process($cmd); - // $io->text($process->getCommandLine()); - // if (!$this->isDryRun) { - // $this->getHelper('process')->mustRun($output, $process, sprintf('There was and error running command: %s', $process->getCommandLine())); - // } - // } - // $io->text('done'); - - $io->newLine(); - $io->success('Congrations on your new release'); - if ($this->isDryRun) { - $io->note([ - 'dry-run was enabled so no files were changed and no code was pushed', - ]); + private function commitAndPushNewChanges(InputInterface $input, OutputInterface $output): void + { + $this->io->section('Commiting and new Pushing Changes'); + $processCommands = [ + ['git', 'add', '.'], + ['git', 'commit', '-m', '"starting next release"'], + ['git', 'push', 'origin', $input->getOption('branch')], + ]; + foreach ($processCommands as $cmd) { + $process = new Process($cmd); + $this->io->text($process->getCommandLine()); + if (!$this->isDryRun) { + $this->getHelper('process')->mustRun($output, $process, sprintf('There was and error running command: %s', $process->getCommandLine())); + } } - return self::SUCCESS; + $cmdInput = new ArrayInput([ + 'command' => 'push', + '--dry-run' => $this->isDryRun, + '--branch' => $input->getOption('branch'), + ]); + $this->getApplication()->doRun($cmdInput, $output); + + $this->io->success('Done'); } } diff --git a/src/SonsOfPHP/Bard/src/JsonFile.php b/src/SonsOfPHP/Bard/src/JsonFile.php index 923eeaf3..911fe6fd 100644 --- a/src/SonsOfPHP/Bard/src/JsonFile.php +++ b/src/SonsOfPHP/Bard/src/JsonFile.php @@ -15,7 +15,9 @@ final class JsonFile public function __construct( private string $filename, - ) {} + ) { + $this->load(); + } private function load(): void { @@ -67,4 +69,9 @@ public function with($operator): self { return $operator->apply($this); } + + public function save(): void + { + file_put_contents($this->filename, $this->toJson()); + } }