Skip to content

Commit

Permalink
Merge pull request #29 from fly-apps/detect_octane
Browse files Browse the repository at this point in the history
Detect octane availability in composer.json, flavors through binaries…
  • Loading branch information
KTanAug21 authored Apr 2, 2024
2 parents eaf6802 + 16ee3da commit 92207a3
Show file tree
Hide file tree
Showing 24 changed files with 587 additions and 19 deletions.
9 changes: 5 additions & 4 deletions app/Commands/GenerateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class GenerateCommand extends Command
{--force : Overwrite existing files}
{--skip : Keep existing files}
{--dev : Include dev dependencies like the local .env file}
{--laravel-version= : Set the laravel version}';
{--laravel-version= : Set the laravel version}
{--path=. : Set the directory to check files in.}';

/**
* The description of the command.
Expand Down Expand Up @@ -48,13 +49,13 @@ public function handle()

// Define the options available to the templates.
$options = [
'octane' => $this->option('octane'),
'build_assets' => ! $this->option('no-assets'),
'dev' => $this->option('dev'),
'laravel_version' => $scan->laravelVersion( $this->options() ),
'fly' => $scan->isForFly(),
];

'octane' => $scan->octaneFlavor( $this->options() )
];

// Define the list of templates to render.
// The key is the template name, and the value is the output file name.
$templates = $scan->templates( $options );
Expand Down
9 changes: 9 additions & 0 deletions app/Services/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,13 @@ public function createFile( $output, $result )
// Create the file, finally!
return file_put_contents($output, implode("\n", $result) . "\n");
}

public function composerJsonContent( $directory )
{
$path = $directory.'/composer.json';

if( !file_exists( $path ) ) return [];

return json_decode( file_get_contents( $path ), 1 );
}
}
42 changes: 34 additions & 8 deletions app/Services/Scanner.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,47 @@ class Scanner
*/
public function laravelVersion( $options )
{
$run = Process::run( 'php artisan --version' );
$version = $run->output();
$version = explode('Laravel Framework', $version);
if( count($version) >1 ){
// From artisan command
return trim($version[1]);
}else if( isset( $options['laravel-version']) && !empty($options['laravel-version']) ){
if( isset( $options['laravel-version']) && !empty($options['laravel-version']) ){
// From options
return trim( $options['laravel-version'], '^' );
}else{
// From detection
$run = Process::run( 'php artisan --version' );
$version = $run->output();
$version = explode('Laravel Framework', $version);
if( count($version) >1 ){
// From artisan command
return trim($version[1]);
}
}

// Default Latest Version
return "11.0.0";
}

/**
* Detect octane setup and flavor
*/
public function octaneFlavor( array $options )
{
$composerContent = (new \App\Services\File())->composerJsonContent( $options['path'] );
$octane = false;

// Detect octane from composer.json
if( isset($composerContent['require']) && isset( $composerContent['require']['laravel/octane'] ) ){

// Determine flavor
if( file_exists( $options['path'].'/frankenphp') ){
return 'frankenphp';
}else if( file_exists( $options['path'].'/rr' ) && file_exists( $options['path'].'/.rr.yaml') ){
return 'roadrunner';
}else{
return 'swoole';
}
}
return $options['octane'];
}

/**
* Scan directory and check if applicable for Fly.io deployment
*/
Expand Down
Binary file modified builds/dockerfile-laravel
Binary file not shown.
4 changes: 4 additions & 0 deletions storage/logs/laravel.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[2024-04-01 17:29:21] development.ERROR: Declaration of LaravelZero\Framework\Commands\BuildCommand::handleSignal(int $signal): int|false must be compatible with Symfony\Component\Console\Command\SignalableCommandInterface::handleSignal(int $signal, int|false $previousExitCode = 0): int|false {"exception":"[object] (Symfony\\Component\\ErrorHandler\\Error\\FatalError(code: 0): Declaration of LaravelZero\\Framework\\Commands\\BuildCommand::handleSignal(int $signal): int|false must be compatible with Symfony\\Component\\Console\\Command\\SignalableCommandInterface::handleSignal(int $signal, int|false $previousExitCode = 0): int|false at /home/admin_kath/development/projects/dockerfile-laravel/vendor/laravel-zero/framework/src/Commands/BuildCommand.php:84)
[stacktrace]
#0 {main}
"}
24 changes: 17 additions & 7 deletions tests/Feature/GenerateCommandTest.php
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
<?php

function ignoreFiles( )
{
return ['composer.json','frankenphp','rr','.rr.yaml'];
}

function getTestOptions( string $directory ): string
{
$composerFile = $directory.'/composer.json';
$composerContent = json_decode( file_get_contents( $composerFile ), 1 );
$composerContent = (new \App\Services\File())->composerJsonContent( $directory );
$composerConfig = $composerContent['require'];

// Matches with options for in App\Commands\GenerateCommand::generate() command
$optionsToCheck = [
'laravel/framework' => 'laravel-version'
'laravel/framework' => 'laravel-version',
];

// Gather options
Expand All @@ -17,6 +22,10 @@ function getTestOptions( string $directory ): string
$optionsFound .= '--'.$option.'="'.$composerConfig[$key].'" ';
}
}

// Set directory to check files in
$optionsFound .= '--path="'.$directory.'"';

return $optionsFound;
}

Expand All @@ -37,7 +46,7 @@ function getTestOptions( string $directory ): string
foreach( $referenceFiles as $reference ){
$failedForMsg = 'Failed for: "'.$reference->getPathName().'"';

if( $reference->getFileName() == 'composer.json' ) continue;
if( in_array( $reference->getFileName(), ignoreFiles()) ) continue;

// Second assert: a new file with the reference file's name was created-it should exist!
$this->assertFileExists( $reference->getFileName(), $failedForMsg );
Expand All @@ -46,12 +55,13 @@ function getTestOptions( string $directory ): string
$expected = file_get_contents( $reference->getPathName() ); // expected content from reference file
$generated = file_get_contents( $reference->getFileName() ); // new file content

// Clean UP: Delete generated file, no longer needed
unlink( $reference->getFileName() );

// Third assert: contents are the same
// TODO: ignore different ARG VALUES
$this->assertEquals( $expected, $generated, $failedForMsg);

// Clean UP: Delete generated file, no longer needed
unlink( $reference->getFileName() );

}
}
});
Expand Down
79 changes: 79 additions & 0 deletions tests/Feature/Supported/10_octane_frankenphp/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# syntax = docker/dockerfile:experimental

ARG PHP_VERSION=8.2
ARG NODE_VERSION=18
FROM fideloper/fly-laravel:${PHP_VERSION} as base

# PHP_VERSION needs to be repeated here
# See https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
ARG PHP_VERSION

LABEL fly_launch_runtime="laravel"

# copy application code, skipping files based on .dockerignore
COPY . /var/www/html

RUN composer install --optimize-autoloader --no-dev \
&& mkdir -p storage/logs \
&& php artisan optimize:clear \
&& chown -R www-data:www-data /var/www/html \
&& echo "MAILTO=\"\"\n* * * * * www-data /usr/bin/php /var/www/html/artisan schedule:run" > /etc/cron.d/laravel \
&& sed -i 's/protected \$proxies/protected \$proxies = "*"/g' app/Http/Middleware/TrustProxies.php;\
if [ -d .fly ]; then cp .fly/entrypoint.sh /entrypoint; chmod +x /entrypoint; fi;

RUN rm -rf /etc/supervisor/conf.d/fpm.conf; \
mv /etc/supervisor/octane-franken.conf /etc/supervisor/conf.d/octane-franken.conf; \
rm -f frankenphp; \
php artisan octane:install --no-interaction --server=frankenphp; \
rm /etc/nginx/sites-enabled/default; \
ln -sf /etc/nginx/sites-available/default-octane /etc/nginx/sites-enabled/default;

# Multi-stage build: Build static assets
# This allows us to not include Node within the final container
FROM node:${NODE_VERSION} as node_modules_go_brrr

RUN mkdir /app

RUN mkdir -p /app
WORKDIR /app
COPY . .
COPY --from=base /var/www/html/vendor /app/vendor

# Use yarn or npm depending on what type of
# lock file we might find. Defaults to
# NPM if no lock file is found.
# Note: We run "production" for Mix and "build" for Vite
RUN if [ -f "vite.config.js" ]; then \
ASSET_CMD="build"; \
else \
ASSET_CMD="production"; \
fi; \
if [ -f "yarn.lock" ]; then \
yarn install --frozen-lockfile; \
yarn $ASSET_CMD; \
elif [ -f "pnpm-lock.yaml" ]; then \
corepack enable && corepack prepare pnpm@latest-8 --activate; \
pnpm install --frozen-lockfile; \
pnpm run $ASSET_CMD; \
elif [ -f "package-lock.json" ]; then \
npm ci --no-audit; \
npm run $ASSET_CMD; \
else \
npm install; \
npm run $ASSET_CMD; \
fi;

# From our base container created above, we
# create our final image, adding in static
# assets that we generated above
FROM base

# Packages like Laravel Nova may have added assets to the public directory
# or maybe some custom assets were added manually! Either way, we merge
# in the assets we generated above rather than overwrite them
COPY --from=node_modules_go_brrr /app/public /var/www/html/public-npm
RUN rsync -ar /var/www/html/public-npm/ /var/www/html/public/ \
&& rm -rf /var/www/html/public-npm \
&& chown -R www-data:www-data /var/www/html/public

EXPOSE 8080
6 changes: 6 additions & 0 deletions tests/Feature/Supported/10_octane_frankenphp/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"require": {
"laravel/framework": "^10.0.0",
"laravel/octane" : "*"
}
}
1 change: 1 addition & 0 deletions tests/Feature/Supported/10_octane_frankenphp/frankenphp
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test
1 change: 1 addition & 0 deletions tests/Feature/Supported/10_octane_rr/.rr.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rryaml
79 changes: 79 additions & 0 deletions tests/Feature/Supported/10_octane_rr/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# syntax = docker/dockerfile:experimental

ARG PHP_VERSION=8.2
ARG NODE_VERSION=18
FROM fideloper/fly-laravel:${PHP_VERSION} as base

# PHP_VERSION needs to be repeated here
# See https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
ARG PHP_VERSION

LABEL fly_launch_runtime="laravel"

# copy application code, skipping files based on .dockerignore
COPY . /var/www/html

RUN composer install --optimize-autoloader --no-dev \
&& mkdir -p storage/logs \
&& php artisan optimize:clear \
&& chown -R www-data:www-data /var/www/html \
&& echo "MAILTO=\"\"\n* * * * * www-data /usr/bin/php /var/www/html/artisan schedule:run" > /etc/cron.d/laravel \
&& sed -i 's/protected \$proxies/protected \$proxies = "*"/g' app/Http/Middleware/TrustProxies.php;\
if [ -d .fly ]; then cp .fly/entrypoint.sh /entrypoint; chmod +x /entrypoint; fi;

RUN rm -rf /etc/supervisor/conf.d/fpm.conf; \
mv /etc/supervisor/octane-rr.conf /etc/supervisor/conf.d/octane-rr.conf; \
if [ -f ./vendor/bin/rr ]; then ./vendor/bin/rr get-binary; fi; \
rm -f .rr.yaml; \
rm /etc/nginx/sites-enabled/default; \
ln -sf /etc/nginx/sites-available/default-octane /etc/nginx/sites-enabled/default;

# Multi-stage build: Build static assets
# This allows us to not include Node within the final container
FROM node:${NODE_VERSION} as node_modules_go_brrr

RUN mkdir /app

RUN mkdir -p /app
WORKDIR /app
COPY . .
COPY --from=base /var/www/html/vendor /app/vendor

# Use yarn or npm depending on what type of
# lock file we might find. Defaults to
# NPM if no lock file is found.
# Note: We run "production" for Mix and "build" for Vite
RUN if [ -f "vite.config.js" ]; then \
ASSET_CMD="build"; \
else \
ASSET_CMD="production"; \
fi; \
if [ -f "yarn.lock" ]; then \
yarn install --frozen-lockfile; \
yarn $ASSET_CMD; \
elif [ -f "pnpm-lock.yaml" ]; then \
corepack enable && corepack prepare pnpm@latest-8 --activate; \
pnpm install --frozen-lockfile; \
pnpm run $ASSET_CMD; \
elif [ -f "package-lock.json" ]; then \
npm ci --no-audit; \
npm run $ASSET_CMD; \
else \
npm install; \
npm run $ASSET_CMD; \
fi;

# From our base container created above, we
# create our final image, adding in static
# assets that we generated above
FROM base

# Packages like Laravel Nova may have added assets to the public directory
# or maybe some custom assets were added manually! Either way, we merge
# in the assets we generated above rather than overwrite them
COPY --from=node_modules_go_brrr /app/public /var/www/html/public-npm
RUN rsync -ar /var/www/html/public-npm/ /var/www/html/public/ \
&& rm -rf /var/www/html/public-npm \
&& chown -R www-data:www-data /var/www/html/public

EXPOSE 8080
6 changes: 6 additions & 0 deletions tests/Feature/Supported/10_octane_rr/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"require": {
"laravel/framework": "^10.0.0",
"laravel/octane" : "*"
}
}
1 change: 1 addition & 0 deletions tests/Feature/Supported/10_octane_rr/rr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rr
Loading

0 comments on commit 92207a3

Please sign in to comment.