diff --git a/src/lib/buildPacks/common.ts b/src/lib/buildPacks/common.ts index eb67a73bd..4c1b6053b 100644 --- a/src/lib/buildPacks/common.ts +++ b/src/lib/buildPacks/common.ts @@ -107,11 +107,12 @@ export const setDefaultConfiguration = async (data) => { else if (buildPack === 'php') port = 80; else if (buildPack === 'python') port = 8000; } - if (!installCommand && buildPack !== 'static') + if (!installCommand && buildPack !== 'static' && buildPack !== 'laravel') installCommand = template?.installCommand || 'yarn install'; - if (!startCommand && buildPack !== 'static') + if (!startCommand && buildPack !== 'static' && buildPack !== 'laravel') startCommand = template?.startCommand || 'yarn start'; - if (!buildCommand && buildPack !== 'static') buildCommand = template?.buildCommand || null; + if (!buildCommand && buildPack !== 'static' && buildPack !== 'laravel') + buildCommand = template?.buildCommand || null; if (!publishDirectory) publishDirectory = template?.publishDirectory || null; if (baseDirectory) { if (!baseDirectory.startsWith('/')) baseDirectory = `/${baseDirectory}`; @@ -206,6 +207,71 @@ export async function copyBaseConfigurationFiles( } } + ` + ); + } else if (buildPack === 'laravel') { + await fs.writeFile( + `${workdir}/nginx.conf`, + `user nginx; + worker_processes auto; + + error_log /docker.stdout; + pid /run/nginx.pid; + + events { + worker_connections 1024; + } + + http { + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /docker.stdout main; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + server { + listen 80; + server_name localhost; + + add_header X-Frame-Options "SAMEORIGIN"; + add_header X-XSS-Protection "1; mode=block"; + add_header X-Content-Type-Options "nosniff"; + + index index.html index.htm index.php; + + charset utf-8; + + location / { + root /app/public; + try_files $uri $uri/ /index.php?$query_string; + } + + location = /favicon.ico { access_log off; log_not_found off; } + location = /robots.txt { access_log off; log_not_found off; } + + error_page 404 /index.php; + + location ~ \.php$ { + fastcgi_pass 127.0.0.1:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } + + location ~ /\.(?!well-known).* { + deny all; + } + } + } ` ); } @@ -392,6 +458,16 @@ export function setDefaultBaseImage(buildPack) { label: 'webdevops/php-nginx:7.1-alpine' } ]; + const laravelVersions = [ + { + value: 'webdevops/php-nginx:8.0-alpine', + label: 'webdevops/php-nginx:8.0-alpine' + }, + { + value: 'webdevops/php-apache:8.0-alpine', + label: 'webdevops/php-apache:8.0-alpine' + } + ]; let payload = { baseImage: null, baseBuildImage: null, @@ -424,5 +500,11 @@ export function setDefaultBaseImage(buildPack) { payload.baseImage = 'webdevops/php-apache:8.0-alpine'; payload.baseImages = phpVersions; } + if (buildPack === 'laravel') { + payload.baseImage = 'webdevops/php-nginx:8.0-alpine'; + payload.baseBuildImage = 'node:18'; + payload.baseImages = laravelVersions; + payload.baseBuildImages = nodeVersions; + } return payload; } diff --git a/src/lib/buildPacks/index.ts b/src/lib/buildPacks/index.ts index ac31afc16..163aa3a7d 100644 --- a/src/lib/buildPacks/index.ts +++ b/src/lib/buildPacks/index.ts @@ -14,6 +14,7 @@ import astro from './static'; import eleventy from './static'; import python from './python'; import deno from './deno'; +import laravel from './laravel'; export { node, @@ -31,5 +32,6 @@ export { astro, eleventy, python, - deno + deno, + laravel }; diff --git a/src/lib/buildPacks/laravel.ts b/src/lib/buildPacks/laravel.ts new file mode 100644 index 000000000..ce44fa7fe --- /dev/null +++ b/src/lib/buildPacks/laravel.ts @@ -0,0 +1,36 @@ +import { buildCacheImageForLaravel, buildImage } from '$lib/docker'; +import { promises as fs } from 'fs'; + +const createDockerfile = async (data, image): Promise => { + const { workdir, applicationId, tag } = data; + const Dockerfile: Array = []; + + Dockerfile.push(`FROM ${image}`); + Dockerfile.push(`LABEL coolify.image=true`); + Dockerfile.push('WORKDIR /app'); + Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); + Dockerfile.push(`COPY composer.* ./`); + Dockerfile.push(`COPY database/ database/`); + Dockerfile.push( + `RUN composer install --ignore-platform-reqs --no-interaction --no-plugins --no-scripts --prefer-dist` + ); + Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/public/js/ /app/public/js/`); + Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/public/css/ /app/public/css/`); + Dockerfile.push( + `COPY --from=${applicationId}:${tag}-cache /app/mix-manifest.json /app/public/mix-manifest.json` + ); + Dockerfile.push(`COPY . ./`); + Dockerfile.push(`EXPOSE 80`); + await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); +}; + +export default async function (data) { + const { baseImage, baseBuildImage } = data; + try { + await buildCacheImageForLaravel(data, baseBuildImage); + await createDockerfile(data, baseImage); + await buildImage(data); + } catch (error) { + throw error; + } +} diff --git a/src/lib/components/common.ts b/src/lib/components/common.ts index 34a7b4067..9f0d16263 100644 --- a/src/lib/components/common.ts +++ b/src/lib/components/common.ts @@ -19,7 +19,7 @@ export const staticDeployments = [ 'astro', 'eleventy' ]; -export const notNodeDeployments = ['php', 'docker', 'rust', 'python', 'deno']; +export const notNodeDeployments = ['php', 'docker', 'rust', 'python', 'deno', 'laravel']; export function getDomain(domain) { return domain?.replace('https://', '').replace('http://', ''); diff --git a/src/lib/components/templates.ts b/src/lib/components/templates.ts index 3ad83a3ba..5166baabc 100644 --- a/src/lib/components/templates.ts +++ b/src/lib/components/templates.ts @@ -162,6 +162,16 @@ export function findBuildPack(pack, packageManager = 'npm') { port: 8000 }; } + if (pack === 'laravel') { + return { + ...metaData, + installCommand: null, + buildCommand: null, + startCommand: null, + publishDirectory: null, + port: 80 + }; + } return { name: 'node', fancyName: 'Node.js', @@ -187,18 +197,25 @@ export const buildPacks = [ hoverColor: 'hover:bg-orange-700', color: 'bg-orange-700' }, - { - name: 'docker', - fancyName: 'Docker', - hoverColor: 'hover:bg-sky-700', - color: 'bg-sky-700' - }, + { name: 'php', fancyName: 'PHP', hoverColor: 'hover:bg-indigo-700', color: 'bg-indigo-700' }, + { + name: 'laravel', + fancyName: 'Laravel', + hoverColor: 'hover:bg-indigo-700', + color: 'bg-indigo-700' + }, + { + name: 'docker', + fancyName: 'Docker', + hoverColor: 'hover:bg-sky-700', + color: 'bg-sky-700' + }, { name: 'svelte', fancyName: 'Svelte', diff --git a/src/lib/docker.ts b/src/lib/docker.ts index 196ee3340..478ae54fb 100644 --- a/src/lib/docker.ts +++ b/src/lib/docker.ts @@ -3,6 +3,34 @@ import { promises as fs } from 'fs'; import { checkPnpm } from './buildPacks/common'; import { saveBuildLog } from './common'; +export async function buildCacheImageForLaravel(data, imageForBuild) { + const { applicationId, tag, workdir, docker, buildId, debug, secrets, pullmergeRequestId } = data; + const Dockerfile: Array = []; + Dockerfile.push(`FROM ${imageForBuild}`); + Dockerfile.push('WORKDIR /app'); + Dockerfile.push(`LABEL coolify.image=true`); + if (secrets.length > 0) { + secrets.forEach((secret) => { + if (secret.isBuildSecret) { + if (pullmergeRequestId) { + if (secret.isPRMRSecret) { + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); + } + } else { + if (!secret.isPRMRSecret) { + Dockerfile.push(`ARG ${secret.name}=${secret.value}`); + } + } + } + }); + } + Dockerfile.push(`COPY *.json *.mix.js /app/`); + Dockerfile.push(`COPY resources /app/resources`); + Dockerfile.push(`RUN yarn install && yarn production`); + await fs.writeFile(`${workdir}/Dockerfile-cache`, Dockerfile.join('\n')); + await buildImage({ applicationId, tag, workdir, docker, buildId, isCache: true, debug }); +} + export async function buildCacheImageWithNode(data, imageForBuild) { const { applicationId, diff --git a/src/routes/applications/[id]/configuration/buildpack.svelte b/src/routes/applications/[id]/configuration/buildpack.svelte index 13e6f8e10..7fe2c95ad 100644 --- a/src/routes/applications/[id]/configuration/buildpack.svelte +++ b/src/routes/applications/[id]/configuration/buildpack.svelte @@ -85,13 +85,14 @@ const composerPHP = files.find( (file) => file.name === 'composer.json' && file.type === 'blob' ); + const laravel = files.find((file) => file.name === 'artisan' && file.type === 'blob'); if (yarnLock) packageManager = 'yarn'; if (pnpmLock) packageManager = 'pnpm'; if (dockerfile) { foundConfig = findBuildPack('docker', packageManager); - } else if (packageJson) { + } else if (packageJson && !laravel) { const path = packageJson.path; const data = await get( `${apiUrl}/v4/projects/${projectId}/repository/files/${path}/raw?ref=${branch}`, @@ -107,8 +108,10 @@ foundConfig = findBuildPack('python'); } else if (indexHtml) { foundConfig = findBuildPack('static', packageManager); - } else if (indexPHP || composerPHP) { + } else if ((indexPHP || composerPHP) && !laravel) { foundConfig = findBuildPack('php'); + } else if (laravel) { + foundConfig = findBuildPack('laravel'); } else { foundConfig = findBuildPack('node', packageManager); } @@ -134,13 +137,14 @@ const composerPHP = files.find( (file) => file.name === 'composer.json' && file.type === 'file' ); + const laravel = files.find((file) => file.name === 'artisan' && file.type === 'file'); if (yarnLock) packageManager = 'yarn'; if (pnpmLock) packageManager = 'pnpm'; if (dockerfile) { foundConfig = findBuildPack('docker', packageManager); - } else if (packageJson) { + } else if (packageJson && !laravel) { const data = await get(`${packageJson.git_url}`, { Authorization: `Bearer ${$gitTokens.githubToken}`, Accept: 'application/vnd.github.v2.raw' @@ -153,8 +157,10 @@ foundConfig = findBuildPack('python'); } else if (indexHtml) { foundConfig = findBuildPack('static', packageManager); - } else if (indexPHP || composerPHP) { + } else if ((indexPHP || composerPHP) && !laravel) { foundConfig = findBuildPack('php'); + } else if (laravel) { + foundConfig = findBuildPack('laravel'); } else { foundConfig = findBuildPack('node', packageManager); } diff --git a/src/routes/applications/[id]/index.svelte b/src/routes/applications/[id]/index.svelte index f2c5aa97c..5f801e05b 100644 --- a/src/routes/applications/[id]/index.svelte +++ b/src/routes/applications/[id]/index.svelte @@ -340,7 +340,7 @@ /> - {#if application.buildCommand || application.buildPack === 'rust'} + {#if application.buildCommand || application.buildPack === 'rust' || application.buildPack === 'laravel'}
{/if} -
-
- - + {#if application.buildPack !== 'laravel'} +
+
+ + +
+
- -
+ {/if} {#if !notNodeDeployments.includes(application.buildPack)}