Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

- Dispatch `GenerationFinishedEvent` once a run completes, carrying the URIs of the written
pages.
- Create the directories of a local `storage` as `0755` (before the umask) instead of Flysystem's
default `0700`, so a web server running as another user can read the generated site. Existing
directories keep their mode: remove them, or run `chmod -R u=rwX,go=rX` on them once (#3).
- Add a `bakslash_static_site.filesystem.visibility` service, to redefine for other modes of a
local `storage`.

## 0.1.0

Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,20 @@ bakslash_static_site:
storage: '@flysystem.storage.static_site'
```

A local `storage` directory and its subdirectories are created as `0755` (before the umask is
applied), so a web server running as another user can read them. For other modes, redefine the
`bakslash_static_site.filesystem.visibility` service:

```yaml
# config/services.yaml
services:
bakslash_static_site.filesystem.visibility:
class: League\Flysystem\UnixVisibility\PortableVisibilityConverter
arguments:
$directoryPublic: 0o775
$defaultForDirectories: public
```

## Serving the generated site

Pages are written as `<path>.<format>`, where the format comes from the route's `_format`, or
Expand Down Expand Up @@ -288,6 +302,7 @@ services:
| `bakslash_static_site.renderer` | `Renderer\RendererInterface` |
| `bakslash_static_site.dumper` | `Dumper\DumperInterface` |
| `bakslash_static_site.filesystem` | Flysystem's `FilesystemOperator` |
| `bakslash_static_site.filesystem.visibility` | Flysystem's `UnixVisibility\VisibilityConverter` (local `storage` only) |

To fail a single page and let the run continue, throw `Exception\RenderingException` from a
renderer or `Exception\DumpingException` from a dumper, and yield a
Expand Down
8 changes: 7 additions & 1 deletion src/BakslashStaticSiteBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@
use Bakslash\StaticSiteBundle\ParamsProvider\ParamsProviderInterface;
use League\Flysystem\Filesystem;
use League\Flysystem\Local\LocalFilesystemAdapter;
use League\Flysystem\UnixVisibility\PortableVisibilityConverter;
use League\Flysystem\Visibility;
use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

final class BakslashStaticSiteBundle extends AbstractBundle
Expand Down Expand Up @@ -76,7 +79,10 @@ private function configureStorage(string $storage, ContainerBuilder $builder): v
return;
}

$builder->register('bakslash_static_site.filesystem.visibility', PortableVisibilityConverter::class)
->setArgument('$defaultForDirectories', Visibility::PUBLIC);

$builder->register('bakslash_static_site.filesystem', Filesystem::class)
->addArgument(new Definition(LocalFilesystemAdapter::class, [$storage]));
->addArgument(new Definition(LocalFilesystemAdapter::class, [$storage, new Reference('bakslash_static_site.filesystem.visibility')]));
}
}
50 changes: 50 additions & 0 deletions tests/Fixtures/LocalStorageKernel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace Bakslash\StaticSiteBundle\Tests\Fixtures;

use Bakslash\StaticSiteBundle\Tests\Fixtures\Controller\ArticleController;
use League\Flysystem\UnixVisibility\PortableVisibilityConverter;
use League\Flysystem\Visibility;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

/**
* Exercises a local storage: modes of nested clean-URL directories, with the bundle's visibility
* converter or a redefined one (in the "custom_visibility" environment).
*/
final class LocalStorageKernel extends TestKernel
{
public const CUSTOM_VISIBILITY_ENV = 'custom_visibility';

public function getOutputDir(): string
{
return $this->getProjectDir().'/local-storage-site';
}

protected function configureContainer(ContainerConfigurator $container): void
{
parent::configureContainer($container);

$container->extension('bakslash_static_site', [
'storage' => $this->getOutputDir(),
'clean_urls' => true,
]);

$services = $container->services();
$services->set(SlugParamsProvider::class)->autoconfigure();
$services->set(ArticleController::class)->tag('controller.service_arguments');

if (self::CUSTOM_VISIBILITY_ENV === $this->environment) {
$services->set('bakslash_static_site.filesystem.visibility', PortableVisibilityConverter::class)
->arg('$directoryPublic', 0o750)
->arg('$defaultForDirectories', Visibility::PUBLIC);
}
}

protected function configureRoutes(RoutingConfigurator $routes): void
{
$routes->import(ArticleController::class, 'attribute');
}
}
66 changes: 66 additions & 0 deletions tests/Functional/LocalStorageTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

namespace Bakslash\StaticSiteBundle\Tests\Functional;

use Bakslash\StaticSiteBundle\Tests\Fixtures\LocalStorageKernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Filesystem\Filesystem;

final class LocalStorageTest extends KernelTestCase
{
protected static function getKernelClass(): string
{
return LocalStorageKernel::class;
}

protected function tearDown(): void
{
if (self::$kernel instanceof LocalStorageKernel) {
(new Filesystem())->remove(self::$kernel->getOutputDir());
}

parent::tearDown();
}

public function testGeneratedDirectoriesAreReadableByOtherUsers(): void
{
$this->assertDirectoryModes('0755', $this->generate([]));
}

public function testDirectoryModesFollowARedefinedVisibilityService(): void
{
$this->assertDirectoryModes('0750', $this->generate(['environment' => LocalStorageKernel::CUSTOM_VISIBILITY_ENV]));
}

/**
* @param array{environment?: string} $options
*/
private function generate(array $options): string
{
$umask = umask(0o022);

try {
$kernel = self::bootKernel($options);
\assert($kernel instanceof LocalStorageKernel);

$tester = new CommandTester((new Application($kernel))->find('static-site:generate'));

$this->assertSame(0, $tester->execute(['--allow-debug' => true]), $tester->getDisplay());
} finally {
umask($umask);
}

return $kernel->getOutputDir();
}

private function assertDirectoryModes(string $expected, string $outputDir): void
{
foreach ([$outputDir, $outputDir.'/articles', $outputDir.'/articles/foo'] as $directory) {
$this->assertSame($expected, \sprintf('%04o', fileperms($directory) & 0o777), $directory);
}
}
}
Loading