Skip to content

Commit 2133bd0

Browse files
committed
Add Detectors
1 parent cb5a786 commit 2133bd0

17 files changed

+378
-33
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
],
1212
"require": {
1313
"php": "^8.0",
14+
"composer/semver": "^3.3",
1415
"illuminate/contracts": "^9.47",
1516
"twig/twig": "^3.0"
1617
},

docker/template/nginx.dockerfile.twig

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
{# import node partial, exposed as "node" layer #}
22
{% include 'partials/node.dockerfile.twig' %}
33

4-
# Final Image
54
FROM nginx:1-alpine
65

76
WORKDIR /app/public
87

9-
{% if node_build_tool == 'vite' %}
10-
{# only copy assets if npm build is enabled #}
11-
COPY --from=node /app/public/build/ /app/public/build/
12-
{% elseif node_build_tool == 'mix' %}
13-
COPY --from=node /app/public/css/ /app/public/css/
14-
COPY --from=node /app/public/js/ /app/public/js/
15-
COPY --from=node /app/public/fonts/ /app/public/fonts/
16-
{% endif %}
8+
{# import node-copy-assets partial, copy assets from node layer #}
9+
{% include 'partials/node-copy-assets.dockerfile.twig' %}
1710

1811
COPY /public/ /app/public/
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{% if node_build_tool %}
2+
# copy {{ node_build_tool }} assets to nginx image
3+
{% if node_build_tool == 'vite' %}
4+
{# only copy assets if npm build is enabled #}
5+
COPY --from=node /app/public/build/ /app/public/build/
6+
{% elseif node_build_tool == 'mix' %}
7+
COPY --from=node /app/public/css/ /app/public/css/
8+
COPY --from=node /app/public/js/ /app/public/js/
9+
COPY --from=node /app/public/fonts/ /app/public/fonts/
10+
{% endif %} {# end of node_build_tool switch #}
11+
{% endif %} {# end of node_build_tool if #}

docker/template/partials/node.dockerfile.twig

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{% if node_package_manager %}
2+
# built frontend assets with {{ node_package_manager }} and {{ node_build_tool }}
23
FROM node:lts-alpine AS node
34
WORKDIR /app
45

@@ -11,18 +12,26 @@ RUN npm ci
1112
{# install dependencies with yarn #}
1213
COPY /package.json /yarn.lock /app/
1314
RUN yarn install
14-
{% endif %}
15+
{% endif %} {# end of node_package_manager switch #}
1516

1617
COPY /resources/ /app/resources/
1718

1819
{# build assets #}
1920
{% if node_build_tool == 'vite' %}
2021
COPY /vite.config.js /app/
2122
{# build with vite #}
23+
{% if node_package_manager == 'npm' %}
2224
RUN npm run build
25+
{% elseif node_package_manager == 'yarn' %}
26+
RUN yarn run build
27+
{% endif %}
2328
{% elseif node_build_tool == 'mix' %}
2429
COPY /webpack.mix.js /app/
2530
{# build with mix #}
31+
{% if node_package_manager == 'npm' %}
2632
RUN npm run production
27-
{% endif %}
28-
{% endif %}
33+
{% elseif node_package_manager == 'yarn' %}
34+
RUN yarn run production
35+
{% endif %} {# end of node_package_manager switch #}
36+
{% endif %} {# end of node_build_tool #}
37+
{% endif %} {# end of node_package_manager if #}

docker/template/php.dockerfile.twig

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,41 @@
22
FROM php:{{ php_version }}-fpm AS composer
33
WORKDIR /app
44

5-
## Install Composer
5+
## install composer dependencies
66
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
77
RUN install-php-extensions @composer
8-
## Install dependencies
98
COPY / /app
109
RUN composer install --optimize-autoloader --no-dev
1110

1211
{# import node partial, exposed as "node" layer #}
1312
{% include 'partials/node.dockerfile.twig' %}
1413

15-
# Final Image
14+
# build php-fpm image
1615
FROM php:{{ php_version }}-fpm
16+
WORKDIR /app
1717

18+
# install required extensions
1819
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
19-
RUN install-php-extensions bcmath pdo_pgsql redis
20-
21-
WORKDIR /app
20+
RUN install-php-extensions {{ php_extensions|join(' ') }}
2221

22+
# copy application source code
2323
COPY / /app
2424

25-
{% if node_build_tool == 'vite' %}
26-
{# only copy assets if npm build is enabled #}
27-
COPY --from=node /app/public/build/ /app/public/build/
28-
{% elseif node_build_tool == 'mix' %}
29-
COPY --from=node /app/public/css/ /app/public/css/
30-
COPY --from=node /app/public/js/ /app/public/js/
31-
COPY --from=node /app/public/fonts/ /app/public/fonts/
32-
{% endif %}
25+
{# import node-copy-assets partial, copy assets from node layer #}
26+
{% include 'partials/node-copy-assets.dockerfile.twig' %}
3327

28+
# copy dependencies from composer installation step
3429
COPY --from=composer /app/vendor/ /app/vendor/
3530

31+
# make storage writable for application
3632
RUN chown --recursive www-data:www-data /app/storage
3733

3834
{% if artisan_optimize %}
35+
# optimize application before start
3936
RUN echo "php artisan optimize --no-ansi && php-fpm" >> /usr/bin/entrypoint.sh && \
4037
chmod +x /usr/bin/entrypoint.sh
4138
{% else %}
39+
# add entrypoint
4240
RUN echo "php-fpm" >> /usr/bin/entrypoint.sh && \
4341
chmod +x /usr/bin/entrypoint.sh
4442
{% endif %}

src/Commands/BaseDockerCommand.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ protected function runProcess(Process $process): int
2323
};
2424
});
2525
}
26-
}
26+
}

src/Commands/Choices/NodeBuildTool.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,12 @@ public static function values(): array
1515
self::MIX,
1616
];
1717
}
18+
19+
public static function name(string $value): string
20+
{
21+
return match ($value) {
22+
self::VITE => 'Vite.js',
23+
self::MIX => 'Laravel mix',
24+
};
25+
}
1826
}

src/Commands/Choices/NodePackageManager.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,12 @@ public static function values(): array
1515
self::YARN,
1616
];
1717
}
18+
19+
public static function name(string $nodePackageManager): string
20+
{
21+
return match ($nodePackageManager) {
22+
self::NPM => 'NPM',
23+
self::YARN => 'Yarn',
24+
};
25+
}
1826
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace BlameButton\LaravelDockerBuilder\Commands\Choices;
4+
5+
class PhpExtensions
6+
{
7+
private const SUPPORTED_EXTENSIONS = 'https://github.com/mlocati/docker-php-extension-installer/raw/master/data/supported-extensions';
8+
9+
private static array|null $cache = null;
10+
11+
public static function values(string $phpVersion = null): array
12+
{
13+
if (! is_null(self::$cache)) {
14+
return self::$cache;
15+
}
16+
17+
try {
18+
$contents = file_get_contents(self::SUPPORTED_EXTENSIONS);
19+
20+
return self::$cache = str($contents)
21+
->explode("\n")
22+
->filter(fn (string $extension): bool => is_null($phpVersion) || str($extension)->contains($phpVersion))
23+
->map(fn (string $extension): string => str($extension)->trim()->before(' '))
24+
->filter()
25+
->toArray();
26+
} catch (\ErrorException) {
27+
return [];
28+
}
29+
}
30+
}

src/Commands/DockerGenerateCommand.php

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,14 @@
55
use BlameButton\LaravelDockerBuilder\Commands\Choices\ArtisanOptimize;
66
use BlameButton\LaravelDockerBuilder\Commands\Choices\NodeBuildTool;
77
use BlameButton\LaravelDockerBuilder\Commands\Choices\NodePackageManager;
8+
use BlameButton\LaravelDockerBuilder\Commands\Choices\PhpExtensions;
89
use BlameButton\LaravelDockerBuilder\Commands\Choices\PhpVersion;
10+
use BlameButton\LaravelDockerBuilder\Detector\NodeBuildToolDetector;
11+
use BlameButton\LaravelDockerBuilder\Detector\NodePackageManagerDetector;
12+
use BlameButton\LaravelDockerBuilder\Detector\PhpExtensionsDetector;
13+
use BlameButton\LaravelDockerBuilder\Detector\PhpVersionDetector;
914
use BlameButton\LaravelDockerBuilder\Traits\InteractsWithTwig;
15+
use Illuminate\Support\Collection;
1016
use Symfony\Component\Console\Input\InputOption;
1117

1218
class DockerGenerateCommand extends BaseCommand
@@ -20,14 +26,27 @@ class DockerGenerateCommand extends BaseCommand
2026
public function handle(): int
2127
{
2228
$phpVersion = $this->getPhpVersion();
29+
$phpExtensions = $this->getPhpExtensions($phpVersion);
2330
$artisanOptimize = $this->getArtisanOptimize();
24-
2531
$nodePackageManager = $this->getNodePackageManager();
2632
$nodeBuildTool = $nodePackageManager ? $this->getNodeBuildTool() : false;
2733

34+
if ($this->option('detect')) {
35+
$this->info('Detected Configuration');
36+
}
37+
38+
$this->table(['Key', 'Value'], [
39+
['PHP version', "<comment>$phpVersion</comment>"],
40+
['PHP extensions', implode(', ', $phpExtensions)],
41+
['Artisan Optimize', '<comment>'.json_encode($artisanOptimize).'</comment>'],
42+
['Node Package Manager', NodePackageManager::name($nodePackageManager)],
43+
['Node Build Tool', $nodePackageManager ? NodeBuildTool::name($nodeBuildTool) : 'None'],
44+
]);
45+
2846
$dockerfiles = collect([
2947
'php.dockerfile' => $this->render('php.dockerfile.twig', [
3048
'php_version' => $phpVersion,
49+
'php_extensions' => $phpExtensions,
3150
'artisan_optimize' => $artisanOptimize,
3251
'node_package_manager' => $nodePackageManager,
3352
'node_build_tool' => $nodeBuildTool,
@@ -65,16 +84,52 @@ private function getPhpVersion(): string
6584
: throw new \InvalidArgumentException("Invalid value [$option] for option [php-version]");
6685
}
6786

87+
$detected = app(PhpVersionDetector::class)->detect();
88+
89+
if ($this->option('detect')) {
90+
return $detected;
91+
}
92+
6893
return $this->choice(
6994
question: 'PHP version',
7095
choices: PhpVersion::values(),
71-
default: PhpVersion::v8_2,
96+
default: $detected ?: PhpVersion::v8_2,
97+
);
98+
}
99+
100+
private function getPhpExtensions(string $phpVersion): array
101+
{
102+
$supportedExtensions = PhpExtensions::values($phpVersion);
103+
104+
if ($option = $this->option('php-extensions')) {
105+
return Collection::make(explode(',', $option))
106+
->intersect($supportedExtensions)
107+
->toArray();
108+
}
109+
110+
$detected = app(PhpExtensionsDetector::class, ['supportedExtensions' => $supportedExtensions])->detect();
111+
112+
if ($this->option('detect')) {
113+
$detected = explode(',', $detected);
114+
115+
foreach ($detected as $key => $value) {
116+
$detected[$key] = $supportedExtensions[$value];
117+
}
118+
119+
return $detected;
120+
}
121+
122+
return $this->choice(
123+
question: 'PHP extensions',
124+
choices: $supportedExtensions,
125+
default: $detected,
126+
multiple: true,
72127
);
73128
}
74129

75130
public function getArtisanOptimize(): bool
76131
{
77-
if ($this->option('optimize')) {
132+
if ($this->option('optimize') || $this->option('detect')) {
78133
return true;
79134
}
80135

@@ -95,10 +150,16 @@ private function getNodePackageManager(): string|false
95150
: throw new \InvalidArgumentException("Invalid value [$option] for option [node-package-manager]");
96151
}
97152

153+
$detected = app(NodePackageManagerDetector::class)->detect();
154+
155+
if ($this->option('detect')) {
156+
return $detected;
157+
}
158+
98159
return $this->optionalChoice(
99160
question: 'Which Node package manager do you use?',
100161
choices: NodePackageManager::values(),
101-
default: NodePackageManager::NPM,
162+
default: $detected ?: NodePackageManager::NPM,
102163
);
103164
}
104165

@@ -110,22 +171,40 @@ private function getNodeBuildTool(): string
110171
: throw new \InvalidArgumentException("Invalid value [$option] for option [node-build-tool]");
111172
}
112173

174+
$detected = app(NodeBuildToolDetector::class)->detect();
175+
176+
if ($this->option('detect')) {
177+
return $detected;
178+
}
179+
113180
return $this->choice(
114181
question: 'Which Node build tool do you use?',
115182
choices: NodeBuildTool::values(),
116-
default: NodeBuildTool::VITE,
183+
default: $detected ?: NodeBuildTool::VITE,
117184
);
118185
}
119186

120187
protected function getOptions(): array
121188
{
122189
return [
190+
new InputOption(
191+
name: 'detect',
192+
shortcut: 'd',
193+
mode: InputOption::VALUE_NONE,
194+
description: 'Detect project requirements'
195+
),
123196
new InputOption(
124197
name: 'php-version',
125198
shortcut: 'p',
126199
mode: InputOption::VALUE_REQUIRED,
127200
description: sprintf('PHP version (supported: %s)', implode(', ', PhpVersion::values())),
128201
),
202+
new InputOption(
203+
name: 'php-extensions',
204+
shortcut: 'e',
205+
mode: InputOption::VALUE_REQUIRED,
206+
description: sprintf('PHP extensions (supported: %s)', implode(', ', PhpExtensions::values())),
207+
),
129208
new InputOption(
130209
name: 'optimize',
131210
shortcut: 'o',

0 commit comments

Comments
 (0)