Skip to content

Commit

Permalink
BRAIN-43 - Templated manifest.xml
Browse files Browse the repository at this point in the history
  • Loading branch information
cyl3x authored and lernhart committed Dec 2, 2024
1 parent 7310912 commit 7195943
Show file tree
Hide file tree
Showing 7 changed files with 293 additions and 21 deletions.
27 changes: 14 additions & 13 deletions .github/workflows/app-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
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 }}
22 changes: 22 additions & 0 deletions .github/workflows/app-zip.yml
Original file line number Diff line number Diff line change
@@ -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'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ public/build
###< vite ###

Resources/app/storefront/dist
manifest.xml
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
81 changes: 81 additions & 0 deletions src/Command/ManifestGenerateCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php declare(strict_types=1);

namespace Swag\Braintree\Command;

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Twig\Environment;

#[AsCommand(name: 'manifest:generate')]
class ManifestGenerateCommand extends Command
{
public function __construct(
#[Autowire(env: 'APP_URL')]
private readonly ?string $appUrl,
#[Autowire(env: 'APP_SECRET')]
private readonly ?string $appSecret,
#[Autowire(param: 'kernel.environment')]
private readonly ?string $environment,
#[Autowire(param: 'kernel.project_dir')]
private readonly ?string $projectDir,
#[Autowire(service: 'twig')]
private readonly Environment $twig,
) {
parent::__construct();
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

if ($this->appUrl === null || $this->appSecret === null || $this->environment === null || $this->projectDir === null) {

Check warning on line 35 in src/Command/ManifestGenerateCommand.php

View workflow job for this annotation

GitHub Actions / infection

Escaped Mutant for Mutator "LogicalOr": @@ @@ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); - if ($this->appUrl === null || $this->appSecret === null || $this->environment === null || $this->projectDir === null) { + if ($this->appUrl === null && $this->appSecret === null || $this->environment === null || $this->projectDir === null) { $io->error('Missing environment variables'); return Command::FAILURE; }

Check warning on line 35 in src/Command/ManifestGenerateCommand.php

View workflow job for this annotation

GitHub Actions / infection

Escaped Mutant for Mutator "LogicalOr": @@ @@ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); - if ($this->appUrl === null || $this->appSecret === null || $this->environment === null || $this->projectDir === null) { + if (($this->appUrl === null || $this->appSecret === null) && $this->environment === null || $this->projectDir === null) { $io->error('Missing environment variables'); return Command::FAILURE; }

Check warning on line 35 in src/Command/ManifestGenerateCommand.php

View workflow job for this annotation

GitHub Actions / infection

Escaped Mutant for Mutator "LogicalOr": @@ @@ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); - if ($this->appUrl === null || $this->appSecret === null || $this->environment === null || $this->projectDir === null) { + if (($this->appUrl === null || $this->appSecret === null || $this->environment === null) && $this->projectDir === null) { $io->error('Missing environment variables'); return Command::FAILURE; }
$io->error('Missing environment variables');

return Command::FAILURE;
}

$manifest = $this->twig->render('manifest.xml.twig', [

Check warning on line 41 in src/Command/ManifestGenerateCommand.php

View workflow job for this annotation

GitHub Actions / infection

Escaped Mutant for Mutator "ArrayItemRemoval": @@ @@ $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']); + $manifest = $this->twig->render('manifest.xml.twig', ['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);
'appUrl' => $this->environment === 'prod' ? 'https://braintree.shopware.com' : $this->appUrl,

Check warning on line 42 in src/Command/ManifestGenerateCommand.php

View workflow job for this annotation

GitHub Actions / infection

Escaped Mutant for Mutator "Identical": @@ @@ $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']); + $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);

Check warning on line 42 in src/Command/ManifestGenerateCommand.php

View workflow job for this annotation

GitHub Actions / infection

Escaped Mutant for Mutator "Ternary": @@ @@ $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']); + $manifest = $this->twig->render('manifest.xml.twig', ['appUrl' => $this->environment === 'prod' ? $this->appUrl : 'https://braintree.shopware.com', '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);
'appSecret' => $this->appSecret,

Check warning on line 43 in src/Command/ManifestGenerateCommand.php

View workflow job for this annotation

GitHub Actions / infection

Escaped Mutant for Mutator "ArrayItem": @@ @@ $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']); + $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);
'isProd' => $this->environment === 'prod',

Check warning on line 44 in src/Command/ManifestGenerateCommand.php

View workflow job for this annotation

GitHub Actions / infection

Escaped Mutant for Mutator "Identical": @@ @@ $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']); + $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);
]);

$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');

Check warning on line 68 in src/Command/ManifestGenerateCommand.php

View workflow job for this annotation

GitHub Actions / infection

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ } protected function configure(): void { - $this->setDescription('Generate the manifest.xml'); + $this->addOption('force', 'f', null, 'Force overwrite'); } protected function manifestExists(): bool
$this->addOption('force', 'f', null, 'Force overwrite');

Check warning on line 69 in src/Command/ManifestGenerateCommand.php

View workflow job for this annotation

GitHub Actions / infection

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ protected function configure(): void { $this->setDescription('Generate the manifest.xml'); - $this->addOption('force', 'f', null, 'Force overwrite'); + } protected function manifestExists(): bool {
}

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;
}
}
17 changes: 10 additions & 7 deletions manifest.xml → templates/manifest.xml.twig
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,32 @@
</meta>

<setup>
<registrationUrl>https://braintree.shopware.com/app/lifecycle/register</registrationUrl>
<registrationUrl>{{ appUrl }}/app/lifecycle/register</registrationUrl>
{% if not isProd %}
<secret>{{ appSecret }}</secret>
{% endif %}
</setup>

<webhooks>
<webhook name="appActivated" url="https://braintree.shopware.com/app/lifecycle/activate" event="app.activated"/>
<webhook name="appDeactivated" url="https://braintree.shopware.com/app/lifecycle/deactivate" event="app.deactivated"/>
<webhook name="appDeleted" url="https://braintree.shopware.com/app/lifecycle/delete" event="app.deleted"/>
<webhook name="appActivated" url="{{ appUrl }}/app/lifecycle/activate" event="app.activated"/>
<webhook name="appDeactivated" url="{{ appUrl }}/app/lifecycle/deactivate" event="app.deactivated"/>
<webhook name="appDeleted" url="{{ appUrl }}/app/lifecycle/delete" event="app.deleted"/>
</webhooks>

<admin>
<base-app-url>https://braintree.shopware.com/admin-sdk</base-app-url>
<base-app-url>{{ appUrl }}/admin-sdk</base-app-url>
</admin>

<gateways>
<checkout>https://braintree.shopware.com/api/gateway/checkout</checkout>
<checkout>{{ appUrl }}/api/gateway/checkout</checkout>
</gateways>

<payments>
<payment-method>
<identifier>credit_card</identifier>
<name>Credit or Debit Card (by Braintree)</name>
<name lang="de-DE">Kredit- oder Debitkarte (von Braintree)</name>
<pay-url>https://braintree.shopware.com/api/pay</pay-url>
<pay-url>{{ appUrl }}/api/pay</pay-url>
<icon>Resources/config/plugin.jpg</icon>
</payment-method>
</payments>
Expand Down
163 changes: 163 additions & 0 deletions tests/unit/Command/ManifestGenerateCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php declare(strict_types=1);

namespace Swag\Braintree\Tests\Unit\Command;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Swag\Braintree\Command\ManifestGenerateCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Twig\Environment;

#[CoversClass(ManifestGenerateCommand::class)]
class ManifestGenerateCommandTest extends TestCase
{
private Environment&MockObject $twig;

private InputInterface&MockObject $input;

private OutputInterface&MockObject $output;

protected function setUp(): void
{
$this->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();
}
}

0 comments on commit 7195943

Please sign in to comment.