From 5388411ff7ba15b054d6b69c4031f435dfd67f30 Mon Sep 17 00:00:00 2001 From: Michel Bade Date: Fri, 29 Nov 2024 13:35:16 +0100 Subject: [PATCH] BRAIN-43 - Templated manifest.xml --- .github/workflows/app-release.yml | 27 +-- .github/workflows/app-zip.yml | 22 +++ .gitignore | 1 + composer.json | 3 +- src/Command/ManifestGenerateCommand.php | 81 +++++++++ manifest.xml => templates/manifest.xml.twig | 17 +- .../Command/ManifestGenerateCommandTest.php | 163 ++++++++++++++++++ 7 files changed, 293 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/app-zip.yml create mode 100644 src/Command/ManifestGenerateCommand.php rename manifest.xml => templates/manifest.xml.twig (86%) create mode 100644 tests/unit/Command/ManifestGenerateCommandTest.php diff --git a/.github/workflows/app-release.yml b/.github/workflows/app-release.yml index c2b1b7e..97ca1ae 100644 --- a/.github/workflows/app-release.yml +++ b/.github/workflows/app-release.yml @@ -3,17 +3,18 @@ on: workflow_dispatch: jobs: - zip: - uses: shopware/github-actions/.github/workflows/build-zip.yml@main - with: - extensionName: SwagBraintreeApp - release: - uses: shopware/github-actions/.github/workflows/store-release.yml@main - with: - extensionName: SwagBraintreeApp - publishOnly: true - secrets: - accountUser: ${{ secrets.SHOPWARE_ACCOUNT_USER }} - accountPassword: ${{ secrets.SHOPWARE_ACCOUNT_PASSWORD }} - ghToken: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-php + - name: Generate manifest.xml + run: composer setup:manifest -- -f --env=prod + - uses: shopware/github-actions/store-release@main + with: + extensionName: SwagBraintreeApp + publishOnly: 'true' + skipCheckout: 'true' + accountUser: ${{ secrets.SHOPWARE_ACCOUNT_USER }} + accountPassword: ${{ secrets.SHOPWARE_ACCOUNT_PASSWORD }} + ghToken: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/app-zip.yml b/.github/workflows/app-zip.yml new file mode 100644 index 0000000..feef27b --- /dev/null +++ b/.github/workflows/app-zip.yml @@ -0,0 +1,22 @@ +name: App Zip +on: + workflow_dispatch: + pull_request: + paths: + - 'Resources/**/*' + - 'templates/manifest.xml.twig' + - '.shopware-extension.yml' + - .github/workflows/app-zip.yml + +jobs: + zip: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-php + - name: Generate manifest.xml + run: composer setup:manifest -- -f --env=prod + - uses: shopware/github-actions/build-zip@main + with: + extensionName: SwagBraintreeApp + skipCheckout: 'true' diff --git a/.gitignore b/.gitignore index 2a697ac..db3b608 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ public/build ###< vite ### Resources/app/storefront/dist +manifest.xml diff --git a/composer.json b/composer.json index 8b34236..d82343d 100644 --- a/composer.json +++ b/composer.json @@ -81,12 +81,13 @@ "bin/console doctrine:schema:drop --force --full-database", "bin/console doctrine:migrations:migrate -n", "@setup:url", - "@setup:test" + "@setup:manifest" ], "setup:url": [ "bin/console setup:url", "npm run dev" ], + "setup:manifest": "bin/console manifest:generate", "setup:test": [ "bin/console doctrine:schema:drop --env=test --force --full-database", "bin/console doctrine:migrations:migrate --env=test -n" diff --git a/src/Command/ManifestGenerateCommand.php b/src/Command/ManifestGenerateCommand.php new file mode 100644 index 0000000..222edec --- /dev/null +++ b/src/Command/ManifestGenerateCommand.php @@ -0,0 +1,81 @@ +appUrl === null || $this->appSecret === null || $this->environment === null || $this->projectDir === null) { + $io->error('Missing environment variables'); + + return Command::FAILURE; + } + + $manifest = $this->twig->render('manifest.xml.twig', [ + 'appUrl' => $this->environment === 'prod' ? 'https://braintree.shopware.com' : $this->appUrl, + 'appSecret' => $this->appSecret, + 'isProd' => $this->environment === 'prod', + ]); + + $write = true; + + if ($this->manifestExists() && !$input->getOption('force')) { + $write = $io->confirm('manifest.xml already exists. Do you want to overwrite it?', false); + } + + if ($write) { + $success = $this->writeManifest($manifest); + + if ($success === false) { + $io->error('Could not write manifest.xml'); + + return Command::FAILURE; + } + } + + return Command::SUCCESS; + } + + protected function configure(): void + { + $this->setDescription('Generate the manifest.xml'); + $this->addOption('force', 'f', null, 'Force overwrite'); + } + + protected function manifestExists(): bool + { + return \file_exists($this->projectDir . '/manifest.xml'); + } + + protected function writeManifest(string $manifest): bool + { + return \file_put_contents($this->projectDir . '/manifest.xml', $manifest) !== false; + } +} diff --git a/manifest.xml b/templates/manifest.xml.twig similarity index 86% rename from manifest.xml rename to templates/manifest.xml.twig index 4d7ae69..0784f7b 100644 --- a/manifest.xml +++ b/templates/manifest.xml.twig @@ -18,21 +18,24 @@ - https://braintree.shopware.com/app/lifecycle/register + {{ appUrl }}/app/lifecycle/register + {% if not isProd %} + {{ appSecret }} + {% endif %} - - - + + + - https://braintree.shopware.com/admin-sdk + {{ appUrl }}/admin-sdk - https://braintree.shopware.com/api/gateway/checkout + {{ appUrl }}/api/gateway/checkout @@ -40,7 +43,7 @@ credit_card Credit or Debit Card (by Braintree) Kredit- oder Debitkarte (von Braintree) - https://braintree.shopware.com/api/pay + {{ appUrl }}/api/pay Resources/config/plugin.jpg diff --git a/tests/unit/Command/ManifestGenerateCommandTest.php b/tests/unit/Command/ManifestGenerateCommandTest.php new file mode 100644 index 0000000..63dd3d5 --- /dev/null +++ b/tests/unit/Command/ManifestGenerateCommandTest.php @@ -0,0 +1,163 @@ +twig = $this->createMock(Environment::class); + $this->input = $this->createMock(InputInterface::class); + $this->output = $this->createMock(OutputInterface::class); + } + + #[DataProvider(methodName: 'provideFailOnAnyParameterMissing')] + public function testFailOnAnyParameterMissing( + ?string $appUrl, + ?string $appSecret, + ?string $environment, + ?string $projectDir, + ): void { + $this->output + ->expects(static::atLeastOnce()) + ->method('writeln'); + + $command = $this->createCommand( + $appUrl, + $appSecret, + $environment, + $projectDir, + ); + + static::assertSame(ManifestGenerateCommand::FAILURE, $command->run($this->input, $this->output)); + } + + public static function provideFailOnAnyParameterMissing(): \Generator + { + yield 'appUrl missing' => [null, 'appSecret', 'environment', 'projectDir']; + yield 'appSecret missing' => ['appUrl', null, 'environment', 'projectDir']; + yield 'environment missing' => ['appUrl', 'appSecret', null, 'projectDir']; + yield 'projectDir missing' => ['appUrl', 'appSecret', 'environment', null]; + + yield 'appUrl and appSecret missing' => [null, null, 'environment', 'projectDir']; + yield 'appUrl and environment missing' => [null, 'appSecret', null, 'projectDir']; + yield 'appUrl and projectDir missing' => [null, 'appSecret', 'environment', null]; + yield 'appSecret and environment missing' => ['appUrl', null, null, 'projectDir']; + yield 'appSecret and projectDir missing' => ['appUrl', null, 'environment', null]; + yield 'environment and projectDir missing' => ['appUrl', 'appSecret', null, null]; + + yield 'only appUrl' => ['appUrl', null, null, null]; + yield 'only appSecret' => [null, 'appSecret', null, null]; + yield 'only environment' => [null, null, 'environment', null]; + yield 'only projectDir' => [null, null, null, 'projectDir']; + + yield 'all missing' => [null, null, null, null]; + } + + public function testRender(): void + { + $this->output + ->expects(static::never()) + ->method('writeln'); + + $this->twig + ->expects(static::once()) + ->method('render') + ->willReturn('manifest'); + + $command = $this->createCommand(); + + $command->expects(static::once())->method('manifestExists')->willReturn(false); + $command->expects(static::once())->method('writeManifest')->willReturn(true); + + static::assertSame(ManifestGenerateCommand::SUCCESS, $command->run($this->input, $this->output)); + } + + public function testRenderUnsuccessfulWrite(): void + { + $this->output + ->expects(static::atLeastOnce()) + ->method('writeln'); + + $this->twig + ->expects(static::once()) + ->method('render') + ->willReturn('manifest'); + + $command = $this->createCommand(); + + $command->expects(static::once())->method('manifestExists')->willReturn(false); + $command->expects(static::once())->method('writeManifest')->willReturn(false); + + static::assertSame(ManifestGenerateCommand::FAILURE, $command->run($this->input, $this->output)); + } + + public function testManifestExists(): void + { + $this->input + ->expects(static::once()) + ->method('getOption') + ->with('force') + ->willReturn(false); + + $command = $this->createCommand(); + + $command->expects(static::once())->method('manifestExists')->willReturn(true); + $command->expects(static::never())->method('writeManifest'); + + static::assertSame(ManifestGenerateCommand::SUCCESS, $command->run($this->input, $this->output)); + } + + public function testManifestExistsWithForce(): void + { + $this->input + ->expects(static::once()) + ->method('getOption') + ->with('force') + ->willReturn(true); + + $this->output + ->expects(static::never()) + ->method('writeln'); + + $this->twig + ->expects(static::once()) + ->method('render') + ->willReturn('manifest'); + + $command = $this->createCommand(); + + $command->expects(static::once())->method('manifestExists')->willReturn(true); + $command->expects(static::once())->method('writeManifest')->willReturn(true); + + static::assertSame(ManifestGenerateCommand::SUCCESS, $command->run($this->input, $this->output)); + } + + protected function createCommand( + ?string $appUrl = 'appUrl', + ?string $appSecret = 'appSecret', + ?string $environment = 'environment', + ?string $projectDir = 'projectDir', + ): ManifestGenerateCommand&MockObject { + return $this->getMockBuilder(ManifestGenerateCommand::class) + ->onlyMethods(['manifestExists', 'writeManifest']) + ->setConstructorArgs([$appUrl, $appSecret, $environment, $projectDir, $this->twig]) + ->getMock(); + } +}