mirror of
https://github.com/opdavies/build-configs.git
synced 2025-01-22 10:17:32 +00:00
refactor(*): change to a Symfony Console app
This commit is contained in:
parent
4af661bad4
commit
8db64458b1
20
.env
Normal file
20
.env
Normal file
|
@ -0,0 +1,20 @@
|
|||
# In all environments, the following files are loaded if they exist,
|
||||
# the latter taking precedence over the former:
|
||||
#
|
||||
# * .env contains default values for the environment variables needed by the app
|
||||
# * .env.local uncommitted file with local overrides
|
||||
# * .env.$APP_ENV committed environment-specific defaults
|
||||
# * .env.$APP_ENV.local uncommitted environment-specific overrides
|
||||
#
|
||||
# Real environment variables win over .env files.
|
||||
#
|
||||
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
|
||||
#
|
||||
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
|
||||
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
|
||||
|
||||
###> symfony/framework-bundle ###
|
||||
APP_ENV=prod
|
||||
APP_DEBUG=0
|
||||
APP_SECRET=91b4670ae7b240ca6b97ed20f81c81f0
|
||||
###< symfony/framework-bundle ###
|
28
.github/workflows/ci.yml
vendored
28
.github/workflows/ci.yml
vendored
|
@ -1,28 +0,0 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
main:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f # v2.3.4
|
||||
|
||||
- uses: extractions/setup-just@95b912dc5d3ed106a72907f2f9b91e76d60bdb76 # 1.5.0
|
||||
|
||||
- name: Build the Docker image
|
||||
run: |
|
||||
docker image build . \
|
||||
--tag build-configs
|
||||
|
||||
- name: Run PHPStan
|
||||
run: |
|
||||
docker run \
|
||||
--rm \
|
||||
--interactive \
|
||||
--entrypoint phpstan \
|
||||
build-configs \
|
||||
--no-progress
|
13
.gitignore
vendored
13
.gitignore
vendored
|
@ -1,3 +1,12 @@
|
|||
/.phpunit.result.cache
|
||||
/build/
|
||||
/build-configs
|
||||
/**/vendor/
|
||||
|
||||
###> symfony/framework-bundle ###
|
||||
/.env.local
|
||||
/.env.local.php
|
||||
/.env.*.local
|
||||
/config/secrets/prod/prod.decrypt.private.php
|
||||
/public/bundles/
|
||||
/var/
|
||||
/vendor/
|
||||
###< symfony/framework-bundle ###
|
||||
|
|
36
autoload_runtime.template
Normal file
36
autoload_runtime.template
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
// autoload_runtime.php @generated by Symfony Runtime
|
||||
|
||||
if (true === (require_once __DIR__.'/autoload.php') || empty($_SERVER['SCRIPT_FILENAME'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$pharPath = Phar::running();
|
||||
|
||||
if (strlen($pharPath) == 0) {
|
||||
$scriptFileName = $_SERVER['SCRIPT_FILENAME'];
|
||||
} else {
|
||||
$scriptFileName = $_SERVER['APP_SCRIPT_FILENAME'] ?? $_SERVER['SCRIPT_FILENAME'] ?? null;
|
||||
}
|
||||
|
||||
$app = require $scriptFileName;
|
||||
|
||||
if (!is_object($app)) {
|
||||
throw new TypeError(sprintf('Invalid return value: callable object expected, "%s" returned from "%s".', get_debug_type($app), $_SERVER['SCRIPT_FILENAME']));
|
||||
}
|
||||
|
||||
$runtime = $_SERVER['APP_RUNTIME'] ?? $_ENV['APP_RUNTIME'] ?? %runtime_class%;
|
||||
$runtime = new $runtime(($_SERVER['APP_RUNTIME_OPTIONS'] ?? $_ENV['APP_RUNTIME_OPTIONS'] ?? []) + %runtime_options%);
|
||||
|
||||
[$app, $args] = $runtime
|
||||
->getResolver($app)
|
||||
->resolve();
|
||||
|
||||
$app = $app(...$args);
|
||||
|
||||
exit(
|
||||
$runtime
|
||||
->getRunner($app)
|
||||
->run()
|
||||
);
|
|
@ -1,266 +1,19 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
use App\Kernel;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
$_SERVER['APP_SCRIPT_FILENAME'] = __FILE__;
|
||||
|
||||
use Illuminate\Support\{Arr, Collection};
|
||||
use OliverDaviesLtd\BuildConfigs\ConfigurationData;
|
||||
use OliverDaviesLtd\BuildConfigs\DataTransferObject\TemplateFile;
|
||||
use OliverDaviesLtd\BuildConfigs\Enum\{Language, WebServer};
|
||||
use Silly\Application;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
||||
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
|
||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Validator\{ConstraintViolationInterface, Validation};
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
|
||||
$app = new Application();
|
||||
|
||||
$app->command(
|
||||
'init [-p|--project-name=] [-l|--language=] [-t|--type=]',
|
||||
function(
|
||||
string $projectName,
|
||||
string $language,
|
||||
string $type,
|
||||
): void {
|
||||
$projectName = str_replace('.', '-', $projectName);
|
||||
|
||||
// TODO: validate the project type.
|
||||
$output = <<<EOF
|
||||
name: $projectName
|
||||
language: $language
|
||||
type: $type
|
||||
EOF;
|
||||
|
||||
file_put_contents('build.yaml', $output);
|
||||
}
|
||||
)->descriptions('Initialise a new build.yaml file.', [
|
||||
'--project-name' => 'The name of the project.',
|
||||
'--language' => 'The language used in the project.',
|
||||
'--type' => 'The project type.',
|
||||
]);;
|
||||
|
||||
$app->command(
|
||||
'generate [-c|--config-file=] [-o|--output-dir=]',
|
||||
function (
|
||||
SymfonyStyle $io,
|
||||
string $configFile = 'build.yaml',
|
||||
string $outputDir = '.',
|
||||
): void {
|
||||
$configurationData = array_merge(
|
||||
Yaml::parseFile(__DIR__ . '/../resources/build.defaults.yaml'),
|
||||
Yaml::parseFile($configFile),
|
||||
);
|
||||
|
||||
// Convert the input to a configuration data object.
|
||||
$normalizer = new ObjectNormalizer(null, new CamelCaseToSnakeCaseNameConverter());
|
||||
$serializer = new Serializer([$normalizer], [new JsonEncoder()]);
|
||||
$configurationDataObject = $serializer->deserialize(json_encode($configurationData), ConfigurationData::class, 'json');
|
||||
|
||||
$validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
|
||||
$violations = $validator->validate($configurationDataObject);
|
||||
|
||||
if (0 < $violations->count()) {
|
||||
$io->error('Configuration is invalid.');
|
||||
|
||||
$io->listing(
|
||||
collect($violations)
|
||||
->map(fn (ConstraintViolationInterface $v) => "{$v->getPropertyPath()} - {$v->getMessage()}")
|
||||
->toArray()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($configurationData['docker-compose'])) {
|
||||
$configurationData['dockerCompose'] = $configurationData['docker-compose'];
|
||||
$configurationData['docker-compose'] = null;
|
||||
}
|
||||
|
||||
$configurationData['managedText'] = 'Do not edit this file. It is automatically generated by https://www.oliverdavies.uk/build-configs.';
|
||||
|
||||
$filesToGenerate = getFiles(configurationData: $configurationData);
|
||||
|
||||
$io->info("Building configuration for {$configurationData['name']}.");
|
||||
|
||||
$io->write('Generated files:');
|
||||
$io->listing(getListOfFiles(filesToGenerate: $filesToGenerate)->toArray());
|
||||
|
||||
generateFiles(
|
||||
configurationData: $configurationData,
|
||||
filesToGenerate: $filesToGenerate,
|
||||
outputDir: $outputDir,
|
||||
);
|
||||
}
|
||||
)->descriptions('Generate project-specific configuration files.', [
|
||||
'--config-file' => 'The path to the project\'s build.yaml file',
|
||||
'--output-dir' => 'The directory to create files in',
|
||||
]);
|
||||
|
||||
$app->run();
|
||||
|
||||
/**
|
||||
* @param array<string, string> $configurationData
|
||||
* @param Collection<int, TemplateFile> $filesToGenerate
|
||||
*/
|
||||
function generateFiles(
|
||||
Collection $filesToGenerate,
|
||||
string $outputDir,
|
||||
array $configurationData,
|
||||
): void
|
||||
{
|
||||
$filesystem = new Filesystem();
|
||||
$twig = new Environment(new FilesystemLoader([__DIR__ . '/../templates']));
|
||||
|
||||
$filesToGenerate->each(function(TemplateFile $templateFile) use ($configurationData, $filesystem, $outputDir, $twig): void {
|
||||
if ($templateFile->path !== null) {
|
||||
if (!$filesystem->exists($templateFile->path)) {
|
||||
$filesystem->mkdir("{$outputDir}/{$templateFile->path}");
|
||||
}
|
||||
}
|
||||
|
||||
$sourceFile = "{$templateFile->data}.twig";
|
||||
$outputFile = collect([
|
||||
$outputDir,
|
||||
$templateFile->path,
|
||||
$templateFile->name,
|
||||
])->filter()->implode('/');
|
||||
|
||||
$filesystem->dumpFile($outputFile, $twig->render($sourceFile, $configurationData));
|
||||
});
|
||||
|
||||
// If the Docker entrypoint file is generated, ensure it is executable.
|
||||
if ($filesystem->exists("{$outputDir}/tools/docker/images/php/root/usr/local/bin/docker-entrypoint-php")) {
|
||||
$filesystem->chmod("{$outputDir}/tools/docker/images/php/root/usr/local/bin/docker-entrypoint-php", 0755);
|
||||
}
|
||||
if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) {
|
||||
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".');
|
||||
}
|
||||
|
||||
function getFiles(array $configurationData): Collection
|
||||
{
|
||||
/** @var Collection<int, TemplateFile> */
|
||||
$filesToGenerate = collect([
|
||||
new TemplateFile(data: 'common/.dockerignore', name: '.dockerignore'),
|
||||
new TemplateFile(data: 'common/.hadolint.yaml', name: '.hadolint.yaml'),
|
||||
new TemplateFile(data: 'env.example', name: '.env.example'),
|
||||
]);
|
||||
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
|
||||
|
||||
$extraDatabases = Arr::get($configurationData, 'database.extra_databases', []);
|
||||
if (count($extraDatabases) > 0) {
|
||||
$filesToGenerate[] = new TemplateFile(
|
||||
data: 'extra-databases.sql',
|
||||
name: 'extra-databases.sql',
|
||||
path: 'tools/docker/images/database/root/docker-entrypoint-initdb.d',
|
||||
);
|
||||
}
|
||||
return static function (array $context) {
|
||||
$kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
|
||||
|
||||
if (false !== Arr::get($configurationData, "justfile", true)) {
|
||||
$filesToGenerate[] = new TemplateFile(data: 'justfile', name: 'justfile');
|
||||
}
|
||||
|
||||
if (isset($configurationData['dockerCompose']) && $configurationData['dockerCompose'] !== null) {
|
||||
$filesToGenerate[] = new TemplateFile(data: 'docker-compose.yaml', name: 'docker-compose.yaml');
|
||||
}
|
||||
|
||||
if (isPhp(Arr::get($configurationData, 'language'))) {
|
||||
$filesToGenerate[] = new TemplateFile(data: 'php/Dockerfile', name: 'Dockerfile');
|
||||
$filesToGenerate[] = new TemplateFile(data: 'php/phpcs.xml', name: 'phpcs.xml.dist');
|
||||
$filesToGenerate[] = new TemplateFile(data: 'php/phpunit.xml', name: 'phpunit.xml.dist');
|
||||
$filesToGenerate[] = new TemplateFile(
|
||||
data: 'php/docker-entrypoint-php',
|
||||
name: 'docker-entrypoint-php',
|
||||
path: 'tools/docker/images/php/root/usr/local/bin',
|
||||
);
|
||||
|
||||
if (Arr::has(array: $configurationData, keys: 'php.phpstan')) {
|
||||
$filesToGenerate[] = new TemplateFile(data: 'php/phpstan.neon', name: 'phpstan.neon.dist');
|
||||
}
|
||||
}
|
||||
|
||||
if (isNode(Arr::get($configurationData, 'language'))) {
|
||||
$filesToGenerate[] = new TemplateFile(data: 'node/.yarnrc', name: '.yarnrc');
|
||||
$filesToGenerate[] = new TemplateFile(data: 'node/Dockerfile', name: 'Dockerfile');
|
||||
}
|
||||
|
||||
if (isCaddy(Arr::get($configurationData, 'web.type'))) {
|
||||
$filesToGenerate[] = new TemplateFile(
|
||||
data: 'web/caddy/Caddyfile',
|
||||
name: 'Caddyfile',
|
||||
path: 'tools/docker/images/web/root/etc/caddy',
|
||||
);
|
||||
}
|
||||
|
||||
if (isNginx(Arr::get($configurationData, 'web.type'))) {
|
||||
$filesToGenerate[] = new TemplateFile(
|
||||
data: 'web/nginx/default.conf',
|
||||
name: 'default.conf',
|
||||
path: 'tools/docker/images/web/root/etc/nginx/conf.d',
|
||||
);
|
||||
}
|
||||
|
||||
if ('drupal-project' === Arr::get($configurationData, 'type')) {
|
||||
// Add a Drupal version of phpunit.xml.dist.
|
||||
$filesToGenerate[] = new TemplateFile(data: 'drupal-project/phpunit.xml.dist', name: 'phpunit.xml.dist');
|
||||
}
|
||||
|
||||
if (Arr::get($configurationData, 'experimental.createGitHubActionsConfiguration', false) === true) {
|
||||
$filesToGenerate[] = new TemplateFile(
|
||||
data: 'ci/github-actions/ci.yml',
|
||||
name: 'ci.yml',
|
||||
path: '.github/workflows',
|
||||
);
|
||||
}
|
||||
|
||||
return $filesToGenerate;
|
||||
}
|
||||
|
||||
function getListOfFiles(Collection $filesToGenerate): Collection
|
||||
{
|
||||
return $filesToGenerate
|
||||
->map(fn (TemplateFile $templateFile): string =>
|
||||
collect([$templateFile->path, $templateFile->name])->filter()->implode('/'))
|
||||
->unique()
|
||||
->sort();
|
||||
}
|
||||
|
||||
function isCaddy(?string $webServer): bool
|
||||
{
|
||||
if (is_null($webServer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strtoupper($webServer) === WebServer::CADDY->name;
|
||||
}
|
||||
|
||||
function isNginx(?string $webServer): bool
|
||||
{
|
||||
if (is_null($webServer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strtoupper($webServer) === WebServer::NGINX->name;
|
||||
}
|
||||
|
||||
function isNode(?string $language): bool
|
||||
{
|
||||
if (is_null($language)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strtoupper($language) === Language::NODE->name;
|
||||
}
|
||||
|
||||
function isPhp(?string $language): bool
|
||||
{
|
||||
if (is_null($language)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strtoupper($language) === Language::PHP->name;
|
||||
}
|
||||
return new Application($kernel);
|
||||
};
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
{
|
||||
"compression": "GZ",
|
||||
"directories": [
|
||||
"src",
|
||||
"templates",
|
||||
"vendor"
|
||||
],
|
||||
"main": "bin/build-configs",
|
||||
"output": "build/build-configs"
|
||||
"output": "build-configs",
|
||||
"files-bin": [
|
||||
".env.local.php",
|
||||
"autoload_runtime.template",
|
||||
"src/Controller/.gitignore"
|
||||
],
|
||||
"directories": [
|
||||
"config",
|
||||
"public",
|
||||
"resources",
|
||||
"var"
|
||||
],
|
||||
"force-autodiscovery": true,
|
||||
"check-requirements": false,
|
||||
"exclude-composer-files": false,
|
||||
"compression": "GZ"
|
||||
}
|
||||
|
|
|
@ -1,30 +1,71 @@
|
|||
{
|
||||
"type": "project",
|
||||
"license": "proprietary",
|
||||
"require": {
|
||||
"illuminate/support": "^9.50",
|
||||
"mnapoli/silly": "^1.8",
|
||||
"symfony/console": "^6.2",
|
||||
"symfony/filesystem": "^6.2",
|
||||
"symfony/property-access": "^6.2",
|
||||
"symfony/serializer": "^6.2",
|
||||
"symfony/validator": "^6.2",
|
||||
"symfony/yaml": "^6.2",
|
||||
"twig/twig": "^3.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"humbug/box": "^4.2",
|
||||
"pestphp/pest": "^1.22",
|
||||
"phpstan/phpstan": "^1.9",
|
||||
"symfony/var-dumper": "^6.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"OliverDaviesLtd\\BuildConfigs\\": "src/"
|
||||
}
|
||||
"php": "^8.1",
|
||||
"ext-ctype": "*",
|
||||
"ext-iconv": "*",
|
||||
"doctrine/annotations": "^2.0",
|
||||
"illuminate/collections": "*",
|
||||
"illuminate/support": "^10.8",
|
||||
"phpdocumentor/reflection-docblock": "^5.3",
|
||||
"phpstan/phpdoc-parser": "^1.20",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"symfony/console": "6.2.*",
|
||||
"symfony/dotenv": "6.2.*",
|
||||
"symfony/flex": "^2.0",
|
||||
"symfony/framework-bundle": "6.2.*",
|
||||
"symfony/property-access": "6.2.*",
|
||||
"symfony/property-info": "6.2.*",
|
||||
"symfony/runtime": "6.2.*",
|
||||
"symfony/serializer": "6.2.*",
|
||||
"symfony/twig-bundle": "6.2.*",
|
||||
"symfony/validator": "6.2.*",
|
||||
"symfony/yaml": "6.2.*",
|
||||
"twig/extra-bundle": "^2.12|^3.0",
|
||||
"twig/twig": "^2.12|^3.0"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"platform": {
|
||||
"php": "8.2"
|
||||
},
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true
|
||||
"symfony/flex": true,
|
||||
"symfony/runtime": true,
|
||||
"bamarni/composer-bin-plugin": true
|
||||
}
|
||||
},
|
||||
"bin": ["bin/build-configs"],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "src/"
|
||||
}
|
||||
},
|
||||
"replace": {
|
||||
"symfony/polyfill-ctype": "*",
|
||||
"symfony/polyfill-iconv": "*",
|
||||
"symfony/polyfill-php72": "*",
|
||||
"symfony/polyfill-php73": "*",
|
||||
"symfony/polyfill-php74": "*",
|
||||
"symfony/polyfill-php80": "*",
|
||||
"symfony/polyfill-php81": "*"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/symfony": "*"
|
||||
},
|
||||
"extra": {
|
||||
"bin-dir": "new-bin",
|
||||
"symfony": {
|
||||
"allow-contrib": false,
|
||||
"require": "6.2.*"
|
||||
},
|
||||
"runtime": {
|
||||
"autoload_template": "autoload_runtime.template"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8",
|
||||
"symfony/maker-bundle": "^1.48"
|
||||
}
|
||||
}
|
||||
|
|
5748
composer.lock
generated
5748
composer.lock
generated
File diff suppressed because it is too large
Load diff
8
config/bundles.php
Normal file
8
config/bundles.php
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
|
||||
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
|
||||
];
|
19
config/packages/cache.yaml
Normal file
19
config/packages/cache.yaml
Normal file
|
@ -0,0 +1,19 @@
|
|||
framework:
|
||||
cache:
|
||||
# Unique name of your app: used to compute stable namespaces for cache keys.
|
||||
#prefix_seed: your_vendor_name/app_name
|
||||
|
||||
# The "app" cache stores to the filesystem by default.
|
||||
# The data in this cache should persist between deploys.
|
||||
# Other options include:
|
||||
|
||||
# Redis
|
||||
#app: cache.adapter.redis
|
||||
#default_redis_provider: redis://localhost
|
||||
|
||||
# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
|
||||
#app: cache.adapter.apcu
|
||||
|
||||
# Namespaced pools use the above "app" backend by default
|
||||
#pools:
|
||||
#my.dedicated.cache: null
|
3
config/packages/dev/routing.yaml
Normal file
3
config/packages/dev/routing.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
framework:
|
||||
router:
|
||||
strict_requirements: true
|
17
config/packages/framework.yaml
Normal file
17
config/packages/framework.yaml
Normal file
|
@ -0,0 +1,17 @@
|
|||
# see https://symfony.com/doc/current/reference/configuration/framework.html
|
||||
framework:
|
||||
secret: '%env(APP_SECRET)%'
|
||||
#csrf_protection: true
|
||||
#http_method_override: true
|
||||
|
||||
# Enables session support. Note that the session will ONLY be started if you read or write from it.
|
||||
# Remove or comment this section to explicitly disable session support.
|
||||
session:
|
||||
handler_id: null
|
||||
cookie_secure: auto
|
||||
cookie_samesite: lax
|
||||
|
||||
#esi: true
|
||||
#fragments: true
|
||||
php_errors:
|
||||
log: true
|
3
config/packages/prod/routing.yaml
Normal file
3
config/packages/prod/routing.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
framework:
|
||||
router:
|
||||
strict_requirements: null
|
7
config/packages/routing.yaml
Normal file
7
config/packages/routing.yaml
Normal file
|
@ -0,0 +1,7 @@
|
|||
framework:
|
||||
router:
|
||||
utf8: true
|
||||
|
||||
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
|
||||
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
|
||||
#default_uri: http://localhost
|
4
config/packages/test/framework.yaml
Normal file
4
config/packages/test/framework.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
framework:
|
||||
test: true
|
||||
session:
|
||||
storage_id: session.storage.mock_file
|
3
config/packages/test/routing.yaml
Normal file
3
config/packages/test/routing.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
framework:
|
||||
router:
|
||||
strict_requirements: true
|
13
config/packages/translation.yaml
Normal file
13
config/packages/translation.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
framework:
|
||||
default_locale: en
|
||||
translator:
|
||||
default_path: '%kernel.project_dir%/translations'
|
||||
fallbacks:
|
||||
- en
|
||||
# providers:
|
||||
# crowdin:
|
||||
# dsn: '%env(CROWDIN_DSN)%'
|
||||
# loco:
|
||||
# dsn: '%env(LOCO_DSN)%'
|
||||
# lokalise:
|
||||
# dsn: '%env(LOKALISE_DSN)%'
|
6
config/packages/twig.yaml
Normal file
6
config/packages/twig.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
twig:
|
||||
default_path: '%kernel.project_dir%/templates'
|
||||
|
||||
when@test:
|
||||
twig:
|
||||
strict_variables: true
|
13
config/packages/validator.yaml
Normal file
13
config/packages/validator.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
framework:
|
||||
validation:
|
||||
email_validation_mode: html5
|
||||
|
||||
# Enables validator auto-mapping support.
|
||||
# For instance, basic validation constraints will be inferred from Doctrine's metadata.
|
||||
#auto_mapping:
|
||||
# App\Entity\: []
|
||||
|
||||
when@test:
|
||||
framework:
|
||||
validation:
|
||||
not_compromised_password: false
|
17
config/preload.php
Normal file
17
config/preload.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the box project.
|
||||
*
|
||||
* (c) Kevin Herrera <kevin@herrera.io>
|
||||
* Théo Fidry <theo.fidry@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
if (file_exists(dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php')) {
|
||||
require dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php';
|
||||
}
|
3
config/routes.yaml
Normal file
3
config/routes.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
#index:
|
||||
# path: /
|
||||
# controller: App\Controller\DefaultController::index
|
3
config/routes/dev/framework.yaml
Normal file
3
config/routes/dev/framework.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
_errors:
|
||||
resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
|
||||
prefix: /_error
|
31
config/services.yaml
Normal file
31
config/services.yaml
Normal file
|
@ -0,0 +1,31 @@
|
|||
# This file is the entry point to configure your own services.
|
||||
# Files in the packages/ subdirectory configure your dependencies.
|
||||
|
||||
# Put parameters here that don't need to change on each machine where the app is deployed
|
||||
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
|
||||
parameters:
|
||||
|
||||
services:
|
||||
# default configuration for services in *this* file
|
||||
_defaults:
|
||||
autowire: true # Automatically injects dependencies in your services.
|
||||
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
|
||||
|
||||
# makes classes in src/ available to be used as services
|
||||
# this creates a service per class whose id is the fully-qualified class name
|
||||
App\:
|
||||
resource: '../src/'
|
||||
exclude:
|
||||
- '../src/DependencyInjection/'
|
||||
- '../src/Entity/'
|
||||
- '../src/Kernel.php'
|
||||
- '../src/Tests/'
|
||||
|
||||
# controllers are imported separately to make sure services can be injected
|
||||
# as action arguments even if you don't extend any base controller class
|
||||
App\Controller\:
|
||||
resource: '../src/Controller/'
|
||||
tags: ['controller.service_arguments']
|
||||
|
||||
# add more service definitions when explicit configuration is needed
|
||||
# please note that last definitions always *replace* previous ones
|
3
justfile
3
justfile
|
@ -1,5 +1,6 @@
|
|||
_default:
|
||||
@just --list
|
||||
|
||||
build:
|
||||
compile:
|
||||
composer dump-env prod
|
||||
./vendor/bin/box compile
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
parameters:
|
||||
level: 8
|
||||
level: 5
|
||||
paths:
|
||||
- bin
|
||||
- src
|
||||
|
|
18
phpunit.xml
18
phpunit.xml
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Test Suite">
|
||||
<directory suffix="Test.php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">./app</directory>
|
||||
<directory suffix=".php">./src</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
</phpunit>
|
34
public/index.php
Normal file
34
public/index.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the box project.
|
||||
*
|
||||
* (c) Kevin Herrera <kevin@herrera.io>
|
||||
* Théo Fidry <theo.fidry@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
use App\Kernel;
|
||||
use Symfony\Component\Dotenv\Dotenv;
|
||||
use Symfony\Component\ErrorHandler\Debug;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
require dirname(__DIR__).'/vendor/autoload.php';
|
||||
|
||||
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
|
||||
|
||||
if ($_SERVER['APP_DEBUG']) {
|
||||
umask(0);
|
||||
|
||||
Debug::enable();
|
||||
}
|
||||
|
||||
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
|
||||
$request = Request::createFromGlobals();
|
||||
$response = $kernel->handle($request);
|
||||
$response->send();
|
||||
$kernel->terminate($request, $response);
|
269
src/Command/GenerateCommand.php
Normal file
269
src/Command/GenerateCommand.php
Normal file
|
@ -0,0 +1,269 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\DataTransferObject\Config;
|
||||
use App\DataTransferObject\TemplateFile;
|
||||
use App\Enum\Language;
|
||||
use App\Enum\WebServer;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symfony\Component\Serializer\Encoder\JsonEncoder;
|
||||
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
|
||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Validator\ConstraintViolationInterface;
|
||||
use Symfony\Component\Validator\Validation;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Twig\Environment;
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'app:generate',
|
||||
description: 'Generate project-specific configuration files',
|
||||
)]
|
||||
class GenerateCommand extends Command
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addOption(
|
||||
name: 'config-file',
|
||||
shortcut: ['c'],
|
||||
mode: InputOption::VALUE_REQUIRED,
|
||||
description: 'The path to the project\'s build.yaml file',
|
||||
default: 'build.yaml',
|
||||
)
|
||||
->addOption(
|
||||
name: 'output-dir',
|
||||
shortcut: ['o'],
|
||||
mode: InputOption::VALUE_REQUIRED,
|
||||
description: 'The directory to create files in',
|
||||
default: '.',
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$configFile = $input->getOption(name: 'config-file');
|
||||
$outputDir = $input->getOption(name: 'output-dir');
|
||||
|
||||
$configurationData = array_merge(
|
||||
Yaml::parseFile(filename: __DIR__ . '/../../resources/build.defaults.yaml'),
|
||||
Yaml::parseFile(filename: $configFile),
|
||||
);
|
||||
|
||||
// Convert the input to a configuration data object.
|
||||
$normalizer = new ObjectNormalizer(null, new CamelCaseToSnakeCaseNameConverter());
|
||||
$serializer = new Serializer([$normalizer], [new JsonEncoder()]);
|
||||
$configurationDataObject = $serializer->deserialize(json_encode($configurationData), Config::class, 'json');
|
||||
|
||||
$validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
|
||||
$violations = $validator->validate($configurationDataObject);
|
||||
|
||||
if (0 < $violations->count()) {
|
||||
$io->error('Configuration is invalid.');
|
||||
|
||||
$io->listing(
|
||||
collect($violations)
|
||||
->map(fn (ConstraintViolationInterface $v) => "{$v->getPropertyPath()} - {$v->getMessage()}")
|
||||
->toArray()
|
||||
);
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
if (isset($configurationData['docker-compose'])) {
|
||||
$configurationData['dockerCompose'] = $configurationData['docker-compose'];
|
||||
$configurationData['docker-compose'] = null;
|
||||
}
|
||||
|
||||
$configurationData['managedText'] = 'Do not edit this file. It is automatically generated by https://www.oliverdavies.uk/build-configs.';
|
||||
|
||||
$filesToGenerate = $this->getFiles(configurationData: $configurationData);
|
||||
|
||||
$io->info("Building configuration for {$configurationData['name']}.");
|
||||
|
||||
$io->write('Generated files:');
|
||||
$io->listing($this->getListOfFiles(filesToGenerate: $filesToGenerate)->toArray());
|
||||
|
||||
$this->generateFiles(
|
||||
configurationData: $configurationData,
|
||||
filesToGenerate: $filesToGenerate,
|
||||
outputDir: $outputDir,
|
||||
);
|
||||
//
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $configurationData
|
||||
* @param Collection<int, TemplateFile> $filesToGenerate
|
||||
*/
|
||||
function generateFiles(
|
||||
Collection $filesToGenerate,
|
||||
string $outputDir,
|
||||
array $configurationData,
|
||||
): void
|
||||
{
|
||||
$filesystem = new Filesystem();
|
||||
$twig = new Environment(new FilesystemLoader([__DIR__ . '/../../templates']));
|
||||
|
||||
$filesToGenerate->each(function(TemplateFile $templateFile) use ($configurationData, $filesystem, $outputDir, $twig): void {
|
||||
if ($templateFile->path !== null) {
|
||||
if (!$filesystem->exists($templateFile->path)) {
|
||||
$filesystem->mkdir("{$outputDir}/{$templateFile->path}");
|
||||
}
|
||||
}
|
||||
|
||||
$sourceFile = "{$templateFile->data}.twig";
|
||||
$outputFile = collect([
|
||||
$outputDir,
|
||||
$templateFile->path,
|
||||
$templateFile->name,
|
||||
])->filter()->implode('/');
|
||||
|
||||
$filesystem->dumpFile($outputFile, $twig->render($sourceFile, $configurationData));
|
||||
});
|
||||
|
||||
// If the Docker entrypoint file is generated, ensure it is executable.
|
||||
if ($filesystem->exists("{$outputDir}/tools/docker/images/php/root/usr/local/bin/docker-entrypoint-php")) {
|
||||
$filesystem->chmod("{$outputDir}/tools/docker/images/php/root/usr/local/bin/docker-entrypoint-php", 0755);
|
||||
}
|
||||
}
|
||||
|
||||
function getFiles(array $configurationData): Collection
|
||||
{
|
||||
/** @var Collection<int, TemplateFile> */
|
||||
$filesToGenerate = collect([
|
||||
new TemplateFile(data: 'common/.dockerignore', name: '.dockerignore'),
|
||||
new TemplateFile(data: 'common/.hadolint.yaml', name: '.hadolint.yaml'),
|
||||
new TemplateFile(data: 'env.example', name: '.env.example'),
|
||||
]);
|
||||
|
||||
$extraDatabases = Arr::get($configurationData, 'database.extra_databases', []);
|
||||
if (count($extraDatabases) > 0) {
|
||||
$filesToGenerate[] = new TemplateFile(
|
||||
data: 'extra-databases.sql',
|
||||
name: 'extra-databases.sql',
|
||||
path: 'tools/docker/images/database/root/docker-entrypoint-initdb.d',
|
||||
);
|
||||
}
|
||||
|
||||
if (false !== Arr::get($configurationData, "justfile", true)) {
|
||||
$filesToGenerate[] = new TemplateFile(data: 'justfile', name: 'justfile');
|
||||
}
|
||||
|
||||
if (isset($configurationData['dockerCompose']) && $configurationData['dockerCompose'] !== null) {
|
||||
$filesToGenerate[] = new TemplateFile(data: 'docker-compose.yaml', name: 'docker-compose.yaml');
|
||||
}
|
||||
|
||||
if (static::isPhp(Arr::get($configurationData, 'language'))) {
|
||||
$filesToGenerate[] = new TemplateFile(data: 'php/Dockerfile', name: 'Dockerfile');
|
||||
$filesToGenerate[] = new TemplateFile(data: 'php/phpcs.xml', name: 'phpcs.xml.dist');
|
||||
$filesToGenerate[] = new TemplateFile(data: 'php/phpunit.xml', name: 'phpunit.xml.dist');
|
||||
$filesToGenerate[] = new TemplateFile(
|
||||
data: 'php/docker-entrypoint-php',
|
||||
name: 'docker-entrypoint-php',
|
||||
path: 'tools/docker/images/php/root/usr/local/bin',
|
||||
);
|
||||
|
||||
if (Arr::has(array: $configurationData, keys: 'php.phpstan')) {
|
||||
$filesToGenerate[] = new TemplateFile(data: 'php/phpstan.neon', name: 'phpstan.neon.dist');
|
||||
}
|
||||
}
|
||||
|
||||
if (static::isNode(Arr::get($configurationData, 'language'))) {
|
||||
$filesToGenerate[] = new TemplateFile(data: 'node/.yarnrc', name: '.yarnrc');
|
||||
$filesToGenerate[] = new TemplateFile(data: 'node/Dockerfile', name: 'Dockerfile');
|
||||
}
|
||||
|
||||
if (static::isCaddy(Arr::get($configurationData, 'web.type'))) {
|
||||
$filesToGenerate[] = new TemplateFile(
|
||||
data: 'web/caddy/Caddyfile',
|
||||
name: 'Caddyfile',
|
||||
path: 'tools/docker/images/web/root/etc/caddy',
|
||||
);
|
||||
}
|
||||
|
||||
if (static::isNginx(Arr::get($configurationData, 'web.type'))) {
|
||||
$filesToGenerate[] = new TemplateFile(
|
||||
data: 'web/nginx/default.conf',
|
||||
name: 'default.conf',
|
||||
path: 'tools/docker/images/web/root/etc/nginx/conf.d',
|
||||
);
|
||||
}
|
||||
|
||||
if ('drupal-project' === Arr::get($configurationData, 'type')) {
|
||||
// Add a Drupal version of phpunit.xml.dist.
|
||||
$filesToGenerate[] = new TemplateFile(data: 'drupal-project/phpunit.xml.dist', name: 'phpunit.xml.dist');
|
||||
}
|
||||
|
||||
if (Arr::get($configurationData, 'experimental.createGitHubActionsConfiguration', false) === true) {
|
||||
$filesToGenerate[] = new TemplateFile(
|
||||
data: 'ci/github-actions/ci.yml',
|
||||
name: 'ci.yml',
|
||||
path: '.github/workflows',
|
||||
);
|
||||
}
|
||||
|
||||
return $filesToGenerate;
|
||||
}
|
||||
|
||||
function getListOfFiles(Collection $filesToGenerate): Collection
|
||||
{
|
||||
return $filesToGenerate
|
||||
->map(fn (TemplateFile $templateFile): string =>
|
||||
collect([$templateFile->path, $templateFile->name])->filter()->implode('/'))
|
||||
->unique()
|
||||
->sort();
|
||||
}
|
||||
|
||||
function isCaddy(?string $webServer): bool
|
||||
{
|
||||
if (is_null($webServer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strtoupper($webServer) === WebServer::CADDY->name;
|
||||
}
|
||||
|
||||
function isNginx(?string $webServer): bool
|
||||
{
|
||||
if (is_null($webServer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strtoupper($webServer) === WebServer::NGINX->name;
|
||||
}
|
||||
|
||||
function isNode(?string $language): bool
|
||||
{
|
||||
if (is_null($language)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strtoupper($language) === Language::NODE->name;
|
||||
}
|
||||
|
||||
function isPhp(?string $language): bool
|
||||
{
|
||||
if (is_null($language)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strtoupper($language) === Language::PHP->name;
|
||||
}
|
||||
}
|
49
src/Command/InitCommand.php
Normal file
49
src/Command/InitCommand.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'app:init',
|
||||
description: 'Add a short description for your command',
|
||||
)]
|
||||
class InitCommand extends Command
|
||||
{
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addArgument('projectName', InputArgument::REQUIRED, 'The name of the project')
|
||||
->addArgument('language', InputArgument::REQUIRED, 'The language the project uses')
|
||||
->addArgument('type', InputArgument::REQUIRED, 'The type of project')
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
[
|
||||
'language' => $language,
|
||||
'projectName' => $projectName,
|
||||
'type' => $type,
|
||||
] = $input->getArguments();
|
||||
|
||||
$projectName = str_replace('.', '-', $projectName);
|
||||
|
||||
// TODO: validate the project type.
|
||||
$output = <<<EOF
|
||||
name: $projectName
|
||||
language: $language
|
||||
type: $type
|
||||
EOF;
|
||||
|
||||
file_put_contents('build.yaml', $output);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
0
src/Controller/.gitignore
vendored
Normal file
0
src/Controller/.gitignore
vendored
Normal file
|
@ -2,11 +2,11 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OliverDaviesLtd\BuildConfigs;
|
||||
namespace App\DataTransferObject;
|
||||
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
final class ConfigurationData
|
||||
final class Config
|
||||
{
|
||||
/**
|
||||
* @var array<string,string|integer|array<int,string>>
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OliverDaviesLtd\BuildConfigs\DataTransferObject;
|
||||
namespace App\DataTransferObject;
|
||||
|
||||
readonly final class TemplateFile
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OliverDaviesLtd\BuildConfigs\Enum;
|
||||
namespace App\Enum;
|
||||
|
||||
enum Language
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace OliverDaviesLtd\BuildConfigs\Enum;
|
||||
namespace App\Enum;
|
||||
|
||||
enum WebServer
|
||||
{
|
||||
|
|
56
src/Kernel.php
Normal file
56
src/Kernel.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the box project.
|
||||
*
|
||||
* (c) Kevin Herrera <kevin@herrera.io>
|
||||
* Théo Fidry <theo.fidry@gmail.com>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace App;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
|
||||
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
|
||||
use function dirname;
|
||||
|
||||
class Kernel extends BaseKernel
|
||||
{
|
||||
use MicroKernelTrait;
|
||||
|
||||
protected function configureContainer(ContainerConfigurator $container): void
|
||||
{
|
||||
$container->import('../config/{packages}/*.yaml');
|
||||
$container->import('../config/{packages}/'.$this->environment.'/*.yaml');
|
||||
|
||||
if (is_file(dirname(__DIR__).'/config/services.yaml')) {
|
||||
$container->import('../config/services.yaml');
|
||||
$container->import('../config/{services}_'.$this->environment.'.yaml');
|
||||
} elseif (is_file($path = dirname(__DIR__).'/config/services.php')) {
|
||||
(require $path)($container->withPath($path), $this);
|
||||
}
|
||||
}
|
||||
|
||||
protected function configureRoutes(RoutingConfigurator $routes): void
|
||||
{
|
||||
$routes->import('../config/{routes}/'.$this->environment.'/*.yaml');
|
||||
$routes->import('../config/{routes}/*.yaml');
|
||||
|
||||
if (is_file(dirname(__DIR__).'/config/routes.yaml')) {
|
||||
$routes->import('../config/routes.yaml');
|
||||
} elseif (is_file($path = dirname(__DIR__).'/config/routes.php')) {
|
||||
(require $path)($routes->withPath($path), $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function getProjectDir(): string
|
||||
{
|
||||
return __DIR__.'/../';
|
||||
}
|
||||
}
|
218
symfony.lock
Normal file
218
symfony.lock
Normal file
|
@ -0,0 +1,218 @@
|
|||
{
|
||||
"doctrine/annotations": {
|
||||
"version": "2.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "1.10",
|
||||
"ref": "64d8583af5ea57b7afa4aba4b159907f3a148b05"
|
||||
}
|
||||
},
|
||||
"php": {
|
||||
"version": "7.2.9"
|
||||
},
|
||||
"psr/cache": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"psr/container": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"psr/event-dispatcher": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"psr/log": {
|
||||
"version": "1.1.3"
|
||||
},
|
||||
"psr/simple-cache": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"symfony/cache": {
|
||||
"version": "v5.0.11"
|
||||
},
|
||||
"symfony/cache-contracts": {
|
||||
"version": "v2.2.0"
|
||||
},
|
||||
"symfony/config": {
|
||||
"version": "v5.0.11"
|
||||
},
|
||||
"symfony/console": {
|
||||
"version": "5.1",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "5.1",
|
||||
"ref": "c6d02bdfba9da13c22157520e32a602dbee8a75c"
|
||||
},
|
||||
"files": [
|
||||
"bin/console"
|
||||
]
|
||||
},
|
||||
"symfony/contracts": {
|
||||
"version": "v1.0.2"
|
||||
},
|
||||
"symfony/debug": {
|
||||
"version": "v4.2.4"
|
||||
},
|
||||
"symfony/dependency-injection": {
|
||||
"version": "v5.0.11"
|
||||
},
|
||||
"symfony/deprecation-contracts": {
|
||||
"version": "v2.2.0"
|
||||
},
|
||||
"symfony/dotenv": {
|
||||
"version": "v5.0.11"
|
||||
},
|
||||
"symfony/error-handler": {
|
||||
"version": "v5.1.5"
|
||||
},
|
||||
"symfony/event-dispatcher": {
|
||||
"version": "v5.1.5"
|
||||
},
|
||||
"symfony/event-dispatcher-contracts": {
|
||||
"version": "v2.2.0"
|
||||
},
|
||||
"symfony/filesystem": {
|
||||
"version": "v5.0.11"
|
||||
},
|
||||
"symfony/finder": {
|
||||
"version": "v5.0.11"
|
||||
},
|
||||
"symfony/flex": {
|
||||
"version": "1.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "1.0",
|
||||
"ref": "c0eeb50665f0f77226616b6038a9b06c03752d8e"
|
||||
},
|
||||
"files": [
|
||||
".env"
|
||||
]
|
||||
},
|
||||
"symfony/framework-bundle": {
|
||||
"version": "5.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "5.2",
|
||||
"ref": "6ec87563dcc85cd0c48856dcfbfc29610506d250"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/cache.yaml",
|
||||
"config/packages/framework.yaml",
|
||||
"config/packages/test/framework.yaml",
|
||||
"config/preload.php",
|
||||
"config/routes/dev/framework.yaml",
|
||||
"config/services.yaml",
|
||||
"public/index.php",
|
||||
"src/Controller/.gitignore",
|
||||
"src/Kernel.php"
|
||||
]
|
||||
},
|
||||
"symfony/http-client-contracts": {
|
||||
"version": "v2.3.1"
|
||||
},
|
||||
"symfony/http-foundation": {
|
||||
"version": "v5.1.5"
|
||||
},
|
||||
"symfony/http-kernel": {
|
||||
"version": "v5.1.5"
|
||||
},
|
||||
"symfony/maker-bundle": {
|
||||
"version": "1.48",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "1.0",
|
||||
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
|
||||
}
|
||||
},
|
||||
"symfony/polyfill-intl-grapheme": {
|
||||
"version": "v1.22.1"
|
||||
},
|
||||
"symfony/polyfill-intl-normalizer": {
|
||||
"version": "v1.22.1"
|
||||
},
|
||||
"symfony/polyfill-mbstring": {
|
||||
"version": "v1.20.0"
|
||||
},
|
||||
"symfony/polyfill-php73": {
|
||||
"version": "v1.20.0"
|
||||
},
|
||||
"symfony/polyfill-php80": {
|
||||
"version": "v1.20.0"
|
||||
},
|
||||
"symfony/polyfill-php81": {
|
||||
"version": "v1.24.0"
|
||||
},
|
||||
"symfony/routing": {
|
||||
"version": "5.1",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "master",
|
||||
"version": "5.1",
|
||||
"ref": "b4f3e7c95e38b606eef467e8a42a8408fc460c43"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/prod/routing.yaml",
|
||||
"config/packages/routing.yaml",
|
||||
"config/routes.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/service-contracts": {
|
||||
"version": "v2.2.0"
|
||||
},
|
||||
"symfony/string": {
|
||||
"version": "v5.2.6"
|
||||
},
|
||||
"symfony/translation": {
|
||||
"version": "6.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "5.3",
|
||||
"ref": "da64f5a2b6d96f5dc24914517c0350a5f91dee43"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/translation.yaml",
|
||||
"translations/.gitignore"
|
||||
]
|
||||
},
|
||||
"symfony/twig-bundle": {
|
||||
"version": "6.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "5.4",
|
||||
"ref": "bb2178c57eee79e6be0b297aa96fc0c0def81387"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/twig.yaml",
|
||||
"templates/base.html.twig"
|
||||
]
|
||||
},
|
||||
"symfony/validator": {
|
||||
"version": "6.2",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "5.3",
|
||||
"ref": "c32cfd98f714894c4f128bb99aa2530c1227603c"
|
||||
},
|
||||
"files": [
|
||||
"config/packages/validator.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/var-dumper": {
|
||||
"version": "v5.1.5"
|
||||
},
|
||||
"symfony/var-exporter": {
|
||||
"version": "v5.0.11"
|
||||
},
|
||||
"symfony/yaml": {
|
||||
"version": "v5.0.11"
|
||||
},
|
||||
"twig/extra-bundle": {
|
||||
"version": "v3.5.1"
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Test Case
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The closure you provide to your test functions is always bound to a specific PHPUnit test
|
||||
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
|
||||
| need to change it using the "uses()" function to bind a different classes or traits.
|
||||
|
|
||||
*/
|
||||
|
||||
// uses(Tests\TestCase::class)->in('Feature');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Expectations
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When you're writing tests, you often need to check that values meet certain conditions. The
|
||||
| "expect()" function gives you access to a set of "expectations" methods that you can use
|
||||
| to assert different things. Of course, you may extend the Expectation API at any time.
|
||||
|
|
||||
*/
|
||||
|
||||
expect()->extend('toBeOne', function () {
|
||||
return $this->toBe(1);
|
||||
});
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Functions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
|
||||
| project that you don't want to repeat in every file. Here you can also expose helpers as
|
||||
| global functions to help you to reduce the number of lines of code in your test files.
|
||||
|
|
||||
*/
|
||||
|
||||
function something()
|
||||
{
|
||||
// ..
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
use OliverDaviesLtd\BuildConfigs\Validator\ConfigurationValidator;
|
||||
|
||||
beforeEach(function (): void {
|
||||
$this->validator = new ConfigurationValidator();
|
||||
});
|
||||
|
||||
test('The project name should be a string', function (mixed $projectName, int $expectedViolationCount) {
|
||||
$configuration = [
|
||||
'name' => $projectName,
|
||||
];
|
||||
|
||||
expect($this->validator->validate($configuration))
|
||||
->toHaveCount($expectedViolationCount);
|
||||
})->with(function () {
|
||||
yield 'Non-empty string' => ['test', 0];
|
||||
yield 'Empty string' => ['', 1];
|
||||
yield 'Integer' => [1, 1];
|
||||
yield 'Null' => [null, 1];
|
||||
yield 'True' => [true, 1];
|
||||
yield 'False' => [false, 2];
|
||||
});
|
||||
|
||||
test('The project language should be a supported language', function (mixed $language, int $expectedViolationCount) {
|
||||
$configuration = [
|
||||
'language' => $language,
|
||||
];
|
||||
|
||||
expect($this->validator->validate($configuration))
|
||||
->toHaveCount($expectedViolationCount);
|
||||
})->with(function () {
|
||||
yield 'Supported language string' => ['php', 0];
|
||||
yield 'Non-supported language string' => ['not-supported', 1];
|
||||
yield 'Empty string' => ['', 1];
|
||||
yield 'Integer' => [1, 2];
|
||||
yield 'Null' => [null, 1];
|
||||
yield 'True' => [true, 2];
|
||||
yield 'False' => [false, 2];
|
||||
});
|
0
translations/.gitignore
vendored
Normal file
0
translations/.gitignore
vendored
Normal file
5
vendor-bin/box/composer.json
Normal file
5
vendor-bin/box/composer.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"humbug/box": "^4.3"
|
||||
}
|
||||
}
|
3104
vendor-bin/box/composer.lock
generated
Normal file
3104
vendor-bin/box/composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue