feat: select base image for buildpacks

This commit is contained in:
Andras Bacsai 2022-04-26 14:51:08 +02:00
parent 5998212b82
commit d03fbd9224
26 changed files with 359 additions and 109 deletions

View File

@ -13,10 +13,16 @@ https://demo.coolify.io/
Installation is automated with the following command: Installation is automated with the following command:
```bash ```bash
/bin/bash -c "$(curl -fsSL https://get.coollabs.io/coolify/install.sh)" wget -q https://get.coollabs.io/coolify/install.sh -O install.sh; sudo bash ./install.sh
``` ```
If you would like no questions during installation If you would like no questions during installation:
```bash
wget -q https://get.coollabs.io/coolify/install.sh -O install.sh; sudo bash ./install.sh -f
```
For more details goto the [docs](https://docs.coollabs.io/coolify/installation).
## Features ## Features

View File

@ -1,7 +1,7 @@
{ {
"name": "coolify", "name": "coolify",
"description": "An open-source & self-hostable Heroku / Netlify alternative.", "description": "An open-source & self-hostable Heroku / Netlify alternative.",
"version": "2.5.2", "version": "2.5.3",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {
"dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev --host 0.0.0.0", "dev": "docker-compose -f docker-compose-dev.yaml up -d && cross-env NODE_ENV=development & svelte-kit dev --host 0.0.0.0",

View File

@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE "Application" ADD COLUMN "baseBuildImage" TEXT;
ALTER TABLE "Application" ADD COLUMN "baseImage" TEXT;

View File

@ -106,6 +106,7 @@ model Application {
secrets Secret[] secrets Secret[]
persistentStorage ApplicationPersistentStorage[] persistentStorage ApplicationPersistentStorage[]
baseImage String? baseImage String?
baseBuildImage String?
} }
model ApplicationSettings { model ApplicationSettings {

View File

@ -5,6 +5,9 @@ import { scanningTemplates } from '$lib/components/templates';
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
import { staticDeployments } from '$lib/components/common'; import { staticDeployments } from '$lib/components/common';
const staticApps = ['static', 'react', 'vuejs', 'svelte', 'gatsby', 'astro', 'eleventy'];
const nodeBased = ['react', 'vuejs', 'svelte', 'gatsby', 'php', 'astro', 'eleventy', 'node'];
export function makeLabelForStandaloneApplication({ export function makeLabelForStandaloneApplication({
applicationId, applicationId,
fqdn, fqdn,
@ -137,7 +140,13 @@ export const setDefaultConfiguration = async (data) => {
}; };
}; };
export async function copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId) { export async function copyBaseConfigurationFiles(
buildPack,
workdir,
buildId,
applicationId,
baseImage
) {
try { try {
if (buildPack === 'php') { if (buildPack === 'php') {
await fs.writeFile(`${workdir}/entrypoint.sh`, `chown -R 1000 /app`); await fs.writeFile(`${workdir}/entrypoint.sh`, `chown -R 1000 /app`);
@ -146,7 +155,7 @@ export async function copyBaseConfigurationFiles(buildPack, workdir, buildId, ap
buildId, buildId,
applicationId applicationId
}); });
} else if (staticDeployments.includes(buildPack)) { } else if (staticDeployments.includes(buildPack) && baseImage.includes('nginx')) {
await fs.writeFile( await fs.writeFile(
`${workdir}/nginx.conf`, `${workdir}/nginx.conf`,
`user nginx; `user nginx;
@ -199,11 +208,6 @@ export async function copyBaseConfigurationFiles(buildPack, workdir, buildId, ap
} }
` `
); );
await saveBuildLog({
line: 'Copied default configuration file for Nginx.',
buildId,
applicationId
});
} }
} catch (error) { } catch (error) {
console.log(error); console.log(error);
@ -218,3 +222,207 @@ export function checkPnpm(installCommand = null, buildCommand = null, startComma
startCommand?.includes('pnpm') startCommand?.includes('pnpm')
); );
} }
export function setDefaultBaseImage(buildPack) {
const nodeVersions = [
{
value: 'node:lts',
label: 'node:lts'
},
{
value: 'node:18',
label: 'node:18'
},
{
value: 'node:17',
label: 'node:17'
},
{
value: 'node:16',
label: 'node:16'
},
{
value: 'node:14',
label: 'node:14'
},
{
value: 'node:12',
label: 'node:12'
}
];
const staticVersions = [
{
value: 'webdevops/nginx:alpine',
label: 'webdevops/nginx:alpine'
},
{
value: 'webdevops/apache:alpine',
label: 'webdevops/apache:alpine'
}
];
const rustVersions = [
{
value: 'rust:latest',
label: 'rust:latest'
},
{
value: 'rust:1.60',
label: 'rust:1.60'
},
{
value: 'rust:1.60-buster',
label: 'rust:1.60-buster'
},
{
value: 'rust:1.60-bullseye',
label: 'rust:1.60-bullseye'
},
{
value: 'rust:1.60-slim-buster',
label: 'rust:1.60-slim-buster'
},
{
value: 'rust:1.60-slim-bullseye',
label: 'rust:1.60-slim-bullseye'
},
{
value: 'rust:1.60-alpine3.14',
label: 'rust:1.60-alpine3.14'
},
{
value: 'rust:1.60-alpine3.15',
label: 'rust:1.60-alpine3.15'
}
];
const phpVersions = [
{
value: 'webdevops/php-apache:8.0',
label: 'webdevops/php-apache:8.0'
},
{
value: 'webdevops/php-nginx:8.0',
label: 'webdevops/php-nginx:8.0'
},
{
value: 'webdevops/php-apache:7.4',
label: 'webdevops/php-apache:7.4'
},
{
value: 'webdevops/php-nginx:7.4',
label: 'webdevops/php-nginx:7.4'
},
{
value: 'webdevops/php-apache:7.3',
label: 'webdevops/php-apache:7.3'
},
{
value: 'webdevops/php-nginx:7.3',
label: 'webdevops/php-nginx:7.3'
},
{
value: 'webdevops/php-apache:7.2',
label: 'webdevops/php-apache:7.2'
},
{
value: 'webdevops/php-nginx:7.2',
label: 'webdevops/php-nginx:7.2'
},
{
value: 'webdevops/php-apache:7.1',
label: 'webdevops/php-apache:7.1'
},
{
value: 'webdevops/php-nginx:7.1',
label: 'webdevops/php-nginx:7.1'
},
{
value: 'webdevops/php-apache:7.0',
label: 'webdevops/php-apache:7.0'
},
{
value: 'webdevops/php-nginx:7.0',
label: 'webdevops/php-nginx:7.0'
},
{
value: 'webdevops/php-apache:5.6',
label: 'webdevops/php-apache:5.6'
},
{
value: 'webdevops/php-nginx:5.6',
label: 'webdevops/php-nginx:5.6'
},
{
value: 'webdevops/php-apache:8.0-alpine',
label: 'webdevops/php-apache:8.0-alpine'
},
{
value: 'webdevops/php-nginx:8.0-alpine',
label: 'webdevops/php-nginx:8.0-alpine'
},
{
value: 'webdevops/php-apache:7.4-alpine',
label: 'webdevops/php-apache:7.4-alpine'
},
{
value: 'webdevops/php-nginx:7.4-alpine',
label: 'webdevops/php-nginx:7.4-alpine'
},
{
value: 'webdevops/php-apache:7.3-alpine',
label: 'webdevops/php-apache:7.3-alpine'
},
{
value: 'webdevops/php-nginx:7.3-alpine',
label: 'webdevops/php-nginx:7.3-alpine'
},
{
value: 'webdevops/php-apache:7.2-alpine',
label: 'webdevops/php-apache:7.2-alpine'
},
{
value: 'webdevops/php-nginx:7.2-alpine',
label: 'webdevops/php-nginx:7.2-alpine'
},
{
value: 'webdevops/php-apache:7.1-alpine',
label: 'webdevops/php-apache:7.1-alpine'
},
{
value: 'webdevops/php-nginx:7.1-alpine',
label: 'webdevops/php-nginx:7.1-alpine'
}
];
let payload = {
baseImage: null,
baseBuildImage: null,
baseImages: [],
baseBuildImages: []
};
if (nodeBased.includes(buildPack)) {
payload.baseImage = 'node:lts';
payload.baseImages = nodeVersions;
}
if (staticApps.includes(buildPack)) {
payload.baseImage = 'webdevops/nginx:alpine';
payload.baseImages = staticVersions;
payload.baseBuildImage = 'node:lts';
payload.baseBuildImages = nodeVersions;
}
if (buildPack === 'python') {
payload.baseImage = 'python:3-alpine';
}
if (buildPack === 'rust') {
payload.baseImage = 'rust:latest';
payload.baseBuildImage = 'rust:latest';
payload.baseImages = rustVersions;
payload.baseBuildImages = rustVersions;
}
if (buildPack === 'deno') {
payload.baseImage = 'denoland/deno:latest';
}
if (buildPack === 'php') {
payload.baseImage = 'webdevops/php-apache:8.0-alpine';
payload.baseImages = phpVersions;
}
return payload;
}

View File

@ -45,8 +45,8 @@ const createDockerfile = async (data, image): Promise<void> => {
export default async function (data) { export default async function (data) {
try { try {
const image = 'denoland/deno:latest'; const { baseImage, baseBuildImage } = data;
await createDockerfile(data, image); await createDockerfile(data, baseImage);
await buildImage(data); await buildImage(data);
} catch (error) { } catch (error) {
throw error; throw error;

View File

@ -2,25 +2,25 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
const createDockerfile = async (data, imageforBuild): Promise<void> => { const createDockerfile = async (data, imageforBuild): Promise<void> => {
const { applicationId, tag, workdir, publishDirectory } = data; const { applicationId, tag, workdir, publishDirectory, baseImage } = data;
const Dockerfile: Array<string> = []; const Dockerfile: Array<string> = [];
Dockerfile.push(`FROM ${imageforBuild}`); Dockerfile.push(`FROM ${imageforBuild}`);
Dockerfile.push('WORKDIR /app'); Dockerfile.push('WORKDIR /app');
Dockerfile.push(`LABEL coolify.image=true`); Dockerfile.push(`LABEL coolify.image=true`);
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`); Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
if (baseImage.includes('nginx')) {
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
}
Dockerfile.push(`EXPOSE 80`); Dockerfile.push(`EXPOSE 80`);
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
}; };
export default async function (data) { export default async function (data) {
try { try {
const image = 'webdevops/nginx:alpine'; const { baseImage, baseBuildImage } = data;
const imageForBuild = 'node:lts'; await buildCacheImageWithNode(data, baseImage);
await createDockerfile(data, baseBuildImage);
await buildCacheImageWithNode(data, imageForBuild);
await createDockerfile(data, image);
await buildImage(data); await buildImage(data);
} catch (error) { } catch (error) {
throw error; throw error;

View File

@ -22,11 +22,9 @@ const createDockerfile = async (data, image): Promise<void> => {
export default async function (data) { export default async function (data) {
try { try {
const image = 'node:lts'; const { baseImage, baseBuildImage } = data;
const imageForBuild = 'node:lts'; await buildCacheImageWithNode(data, baseBuildImage);
await createDockerfile(data, baseImage);
await buildCacheImageWithNode(data, imageForBuild);
await createDockerfile(data, image);
await buildImage(data); await buildImage(data);
} catch (error) { } catch (error) {
throw error; throw error;

View File

@ -50,8 +50,8 @@ const createDockerfile = async (data, image): Promise<void> => {
export default async function (data) { export default async function (data) {
try { try {
const image = 'node:lts'; const { baseImage, baseBuildImage } = data;
await createDockerfile(data, image); await createDockerfile(data, baseImage);
await buildImage(data); await buildImage(data);
} catch (error) { } catch (error) {
throw error; throw error;

View File

@ -50,8 +50,8 @@ const createDockerfile = async (data, image): Promise<void> => {
export default async function (data) { export default async function (data) {
try { try {
const image = 'node:lts'; const { baseImage, baseBuildImage } = data;
await createDockerfile(data, image); await createDockerfile(data, baseImage);
await buildImage(data); await buildImage(data);
} catch (error) { } catch (error) {
throw error; throw error;

View File

@ -49,8 +49,8 @@ const createDockerfile = async (data, image): Promise<void> => {
export default async function (data) { export default async function (data) {
try { try {
const image = 'node:lts'; const { baseImage, baseBuildImage } = data;
await createDockerfile(data, image); await createDockerfile(data, baseImage);
await buildImage(data); await buildImage(data);
} catch (error) { } catch (error) {
throw error; throw error;

View File

@ -27,7 +27,7 @@ const createDockerfile = async (data, image, htaccessFound): Promise<void> => {
}; };
export default async function (data) { export default async function (data) {
const { workdir, baseDirectory } = data; const { workdir, baseDirectory, baseImage } = data;
try { try {
let htaccessFound = false; let htaccessFound = false;
try { try {
@ -36,10 +36,7 @@ export default async function (data) {
} catch (e) { } catch (e) {
// //
} }
const image = htaccessFound await createDockerfile(data, baseImage, htaccessFound);
? 'webdevops/php-apache:8.0-alpine'
: 'webdevops/php-nginx:8.0-alpine';
await createDockerfile(data, image, htaccessFound);
await buildImage(data); await buildImage(data);
} catch (error) { } catch (error) {
throw error; throw error;

View File

@ -62,8 +62,8 @@ const createDockerfile = async (data, image): Promise<void> => {
export default async function (data) { export default async function (data) {
try { try {
const image = 'python:3-alpine'; const { baseImage, baseBuildImage } = data;
await createDockerfile(data, image); await createDockerfile(data, baseImage);
await buildImage(data); await buildImage(data);
} catch (error) { } catch (error) {
throw error; throw error;

View File

@ -2,24 +2,25 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
const createDockerfile = async (data, image): Promise<void> => { const createDockerfile = async (data, image): Promise<void> => {
const { applicationId, tag, workdir, publishDirectory } = data; const { applicationId, tag, workdir, publishDirectory, baseImage } = data;
const Dockerfile: Array<string> = []; const Dockerfile: Array<string> = [];
Dockerfile.push(`FROM ${image}`); Dockerfile.push(`FROM ${image}`);
Dockerfile.push(`LABEL coolify.image=true`); Dockerfile.push(`LABEL coolify.image=true`);
Dockerfile.push('WORKDIR /app'); Dockerfile.push('WORKDIR /app');
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`); Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
if (baseImage.includes('nginx')) {
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
}
Dockerfile.push(`EXPOSE 80`); Dockerfile.push(`EXPOSE 80`);
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
}; };
export default async function (data) { export default async function (data) {
try { try {
const image = 'webdevops/nginx:alpine'; const { baseImage, baseBuildImage } = data;
const imageForBuild = 'node:lts'; await buildCacheImageWithNode(data, baseBuildImage);
await buildCacheImageWithNode(data, imageForBuild); await createDockerfile(data, baseImage);
await createDockerfile(data, image);
await buildImage(data); await buildImage(data);
} catch (error) { } catch (error) {
throw error; throw error;

View File

@ -27,14 +27,12 @@ const createDockerfile = async (data, image, name): Promise<void> => {
export default async function (data) { export default async function (data) {
try { try {
const { workdir } = data; const { workdir, baseImage, baseBuildImage } = data;
const image = 'rust:latest';
const imageForBuild = 'rust:latest';
const { stdout: cargoToml } = await asyncExecShell(`cat ${workdir}/Cargo.toml`); const { stdout: cargoToml } = await asyncExecShell(`cat ${workdir}/Cargo.toml`);
const parsedToml: any = TOML.parse(cargoToml); const parsedToml: any = TOML.parse(cargoToml);
const name = parsedToml.package.name; const name = parsedToml.package.name;
await buildCacheImageWithCargo(data, imageForBuild); await buildCacheImageWithCargo(data, baseBuildImage);
await createDockerfile(data, image, name); await createDockerfile(data, baseImage, name);
await buildImage(data); await buildImage(data);
} catch (error) { } catch (error) {
throw error; throw error;

View File

@ -10,7 +10,8 @@ const createDockerfile = async (data, image): Promise<void> => {
baseDirectory, baseDirectory,
publishDirectory, publishDirectory,
secrets, secrets,
pullmergeRequestId pullmergeRequestId,
baseImage
} = data; } = data;
const Dockerfile: Array<string> = []; const Dockerfile: Array<string> = [];
@ -37,17 +38,18 @@ const createDockerfile = async (data, image): Promise<void> => {
} else { } else {
Dockerfile.push(`COPY .${baseDirectory || ''} ./`); Dockerfile.push(`COPY .${baseDirectory || ''} ./`);
} }
if (baseImage.includes('nginx')) {
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
}
Dockerfile.push(`EXPOSE 80`); Dockerfile.push(`EXPOSE 80`);
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
}; };
export default async function (data) { export default async function (data) {
try { try {
const image = 'webdevops/nginx:alpine'; const { baseImage, baseBuildImage } = data;
const imageForBuild = 'node:lts'; if (data.buildCommand) await buildCacheImageWithNode(data, baseBuildImage);
if (data.buildCommand) await buildCacheImageWithNode(data, imageForBuild); await createDockerfile(data, baseImage);
await createDockerfile(data, image);
await buildImage(data); await buildImage(data);
} catch (error) { } catch (error) {
throw error; throw error;

View File

@ -2,25 +2,25 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
const createDockerfile = async (data, image): Promise<void> => { const createDockerfile = async (data, image): Promise<void> => {
const { applicationId, tag, workdir, publishDirectory } = data; const { applicationId, tag, workdir, publishDirectory, baseImage } = data;
const Dockerfile: Array<string> = []; const Dockerfile: Array<string> = [];
Dockerfile.push(`FROM ${image}`); Dockerfile.push(`FROM ${image}`);
Dockerfile.push('WORKDIR /app'); Dockerfile.push('WORKDIR /app');
Dockerfile.push(`LABEL coolify.image=true`); Dockerfile.push(`LABEL coolify.image=true`);
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`); Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
if (baseImage.includes('nginx')) {
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
}
Dockerfile.push(`EXPOSE 80`); Dockerfile.push(`EXPOSE 80`);
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
}; };
export default async function (data) { export default async function (data) {
try { try {
const image = 'webdevops/nginx:alpine'; const { baseImage, baseBuildImage } = data;
const imageForBuild = 'node:lts'; await buildCacheImageWithNode(data, baseBuildImage);
await createDockerfile(data, baseImage);
await buildCacheImageWithNode(data, imageForBuild);
await createDockerfile(data, image);
await buildImage(data); await buildImage(data);
} catch (error) { } catch (error) {
throw error; throw error;

View File

@ -2,24 +2,25 @@ import { buildCacheImageWithNode, buildImage } from '$lib/docker';
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
const createDockerfile = async (data, image): Promise<void> => { const createDockerfile = async (data, image): Promise<void> => {
const { applicationId, tag, workdir, publishDirectory } = data; const { applicationId, tag, workdir, publishDirectory, baseImage } = data;
const Dockerfile: Array<string> = []; const Dockerfile: Array<string> = [];
Dockerfile.push(`FROM ${image}`); Dockerfile.push(`FROM ${image}`);
Dockerfile.push('WORKDIR /app'); Dockerfile.push('WORKDIR /app');
Dockerfile.push(`LABEL coolify.image=true`); Dockerfile.push(`LABEL coolify.image=true`);
Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`); Dockerfile.push(`COPY --from=${applicationId}:${tag}-cache /app/${publishDirectory} ./`);
if (baseImage.includes('nginx')) {
Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`); Dockerfile.push(`COPY /nginx.conf /etc/nginx/nginx.conf`);
}
Dockerfile.push(`EXPOSE 80`); Dockerfile.push(`EXPOSE 80`);
await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n')); await fs.writeFile(`${workdir}/Dockerfile`, Dockerfile.join('\n'));
}; };
export default async function (data) { export default async function (data) {
try { try {
const image = 'webdevops/nginx:alpine'; const { baseImage, baseBuildImage } = data;
const imageForBuild = 'node:lts'; await buildCacheImageWithNode(data, baseBuildImage);
await buildCacheImageWithNode(data, imageForBuild); await createDockerfile(data, baseImage);
await createDockerfile(data, image);
await buildImage(data); await buildImage(data);
} catch (error) { } catch (error) {
throw error; throw error;

View File

@ -12,17 +12,7 @@ import type {
Application, Application,
ApplicationPersistentStorage ApplicationPersistentStorage
} from '@prisma/client'; } from '@prisma/client';
const nodeBased = [ import { setDefaultBaseImage } from '$lib/buildPacks/common';
'react',
'vuejs',
'static',
'svelte',
'gatsby',
'php',
'astro',
'eleventy',
'node'
];
export async function listApplications(teamId: string): Promise<Application[]> { export async function listApplications(teamId: string): Promise<Application[]> {
if (teamId === '0') { if (teamId === '0') {
@ -206,26 +196,18 @@ export async function getApplication({ id, teamId }: { id: string; teamId: strin
return s; return s;
}); });
} }
const { baseImage, baseBuildImage, baseBuildImages, baseImages } = setDefaultBaseImage(
body.buildPack
);
// Set default build images // Set default build images
if (!body.baseImage) { if (!body.baseImage) {
if (nodeBased.includes(body.buildPack)) { body.baseImage = baseImage;
body.baseImage = 'node:lts';
} }
if (body.buildPack === 'python') { if (!body.baseBuildImage) {
body.baseImage = 'python:3-alpine'; body.baseBuildImage = baseBuildImage;
} }
if (body.buildPack === 'rust') { return { ...body, baseBuildImages, baseImages };
body.baseImage = 'rust:latest';
}
if (body.buildPack === 'deno') {
body.baseImage = 'denoland/deno:latest';
}
if (body.buildPack === 'php') {
body.baseImage = 'webdevops/php-apache:8.0-alpine';
}
}
return { ...body };
} }
export async function configureGitRepository({ export async function configureGitRepository({
@ -296,7 +278,8 @@ export async function configureApplication({
dockerFileLocation, dockerFileLocation,
denoMainFile, denoMainFile,
denoOptions, denoOptions,
baseImage baseImage,
baseBuildImage
}: { }: {
id: string; id: string;
buildPack: string; buildPack: string;
@ -315,6 +298,7 @@ export async function configureApplication({
denoMainFile: string; denoMainFile: string;
denoOptions: string; denoOptions: string;
baseImage: string; baseImage: string;
baseBuildImage: string;
}): Promise<Application> { }): Promise<Application> {
return await prisma.application.update({ return await prisma.application.update({
where: { id }, where: { id },
@ -334,7 +318,8 @@ export async function configureApplication({
dockerFileLocation, dockerFileLocation,
denoMainFile, denoMainFile,
denoOptions, denoOptions,
baseImage baseImage,
baseBuildImage
} }
}); });
} }

View File

@ -185,6 +185,7 @@
"git_repository": "Git Repository", "git_repository": "Git Repository",
"build_pack": "Build Pack", "build_pack": "Build Pack",
"base_image": "Base Image", "base_image": "Base Image",
"base_build_image": "Base Build Image",
"destination": "Destination", "destination": "Destination",
"application": "Application", "application": "Application",
"url_fqdn": "URL (FQDN)", "url_fqdn": "URL (FQDN)",

View File

@ -47,7 +47,9 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
pythonWSGI, pythonWSGI,
pythonModule, pythonModule,
pythonVariable, pythonVariable,
denoOptions denoOptions,
baseImage,
baseBuildImage
} = job.data; } = job.data;
let { let {
branch, branch,
@ -186,7 +188,7 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
// //
} }
if (!imageFound || deployNeeded) { if (!imageFound || deployNeeded) {
await copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId); await copyBaseConfigurationFiles(buildPack, workdir, buildId, applicationId, baseImage);
if (buildpacks[buildPack]) if (buildpacks[buildPack])
await buildpacks[buildPack]({ await buildpacks[buildPack]({
buildId, buildId,
@ -217,7 +219,9 @@ export default async function (job: Job<BuilderJob, void, string>): Promise<void
pythonVariable, pythonVariable,
dockerFileLocation, dockerFileLocation,
denoMainFile, denoMainFile,
denoOptions denoOptions,
baseImage,
baseBuildImage
}); });
else { else {
await saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId }); await saveBuildLog({ line: `Build pack ${buildPack} not found`, buildId, applicationId });

View File

@ -34,6 +34,8 @@ export type BuilderJob = {
persistentStorage: { path: string }[]; persistentStorage: { path: string }[];
pullmergeRequestId?: unknown; pullmergeRequestId?: unknown;
sourceBranch?: string; sourceBranch?: string;
baseImage: string;
baseBuildImage: string;
}; };
// TODO: Add the other build types // TODO: Add the other build types

View File

@ -63,7 +63,8 @@ export const post: RequestHandler = async (event) => {
dockerFileLocation, dockerFileLocation,
denoMainFile, denoMainFile,
denoOptions, denoOptions,
baseImage baseImage,
baseBuildImage
} = await event.request.json(); } = await event.request.json();
if (port) port = Number(port); if (port) port = Number(port);
if (denoOptions) denoOptions = denoOptions.trim(); if (denoOptions) denoOptions = denoOptions.trim();
@ -98,6 +99,7 @@ export const post: RequestHandler = async (event) => {
denoMainFile, denoMainFile,
denoOptions, denoOptions,
baseImage, baseImage,
baseBuildImage,
...defaultConfiguration ...defaultConfiguration
}); });
return { status: 201 }; return { status: 201 };

View File

@ -33,6 +33,8 @@
gitlabApp: Prisma.GitlabApp; gitlabApp: Prisma.GitlabApp;
gitSource: Prisma.GitSource; gitSource: Prisma.GitSource;
destinationDocker: Prisma.DestinationDocker; destinationDocker: Prisma.DestinationDocker;
baseImages: Array<{ value: string; label: string }>;
baseBuildImages: Array<{ value: string; label: string }>;
}; };
export let isRunning; export let isRunning;
import { page, session } from '$app/stores'; import { page, session } from '$app/stores';
@ -71,11 +73,14 @@
label: 'Gunicorn' label: 'Gunicorn'
} }
]; ];
function containerClass() {
if (!$session.isAdmin || isRunning) {
return 'text-white border border-dashed border-coolgray-300 bg-transparent font-thin px-0';
}
}
if (browser && window.location.hostname === 'demo.coolify.io' && !application.fqdn) { if (browser && window.location.hostname === 'demo.coolify.io' && !application.fqdn) {
application.fqdn = `http://${cuid()}.demo.coolify.io`; application.fqdn = `http://${cuid()}.demo.coolify.io`;
} }
onMount(() => { onMount(() => {
domainEl.focus(); domainEl.focus();
}); });
@ -138,6 +143,14 @@
async function selectWSGI(event) { async function selectWSGI(event) {
application.pythonWSGI = event.detail.value; application.pythonWSGI = event.detail.value;
} }
async function selectBaseImage(event) {
application.baseImage = event.detail.value;
await handleSubmit();
}
async function selectBaseBuildImage(event) {
application.baseBuildImage = event.detail.value;
await handleSubmit();
}
</script> </script>
<div class="flex items-center space-x-2 p-5 px-6 font-bold"> <div class="flex items-center space-x-2 p-5 px-6 font-bold">
@ -310,14 +323,42 @@
/> />
</div> </div>
</div> </div>
<div class="grid grid-cols-2 items-center pb-8"> <div class="grid grid-cols-2 items-center">
<label for="baseImage" class="text-base font-bold text-stone-100" <label for="baseImage" class="text-base font-bold text-stone-100"
>{$t('application.base_image')}</label >{$t('application.base_image')}</label
> >
<div class="no-underline"> <div class="custom-select-wrapper">
<input value={application.baseImage} id="baseImage" disabled class="bg-transparent " /> <Select
isDisabled={!$session.isAdmin || isRunning}
containerClasses={containerClass()}
id="baseImages"
showIndicator={!isRunning}
items={application.baseImages}
on:select={selectBaseImage}
value={application.baseImage}
isClearable={false}
/>
</div> </div>
</div> </div>
{#if application.buildCommand || application.buildPack === 'rust'}
<div class="grid grid-cols-2 items-center pb-8">
<label for="baseBuildImage" class="text-base font-bold text-stone-100"
>{$t('application.base_build_image')}</label
>
<div class="custom-select-wrapper">
<Select
isDisabled={!$session.isAdmin || isRunning}
containerClasses={containerClass()}
id="baseBuildImages"
showIndicator={!isRunning}
items={application.baseBuildImages}
on:select={selectBaseBuildImage}
value={application.baseBuildImage}
isClearable={false}
/>
</div>
</div>
{/if}
</div> </div>
<div class="flex space-x-1 py-5 font-bold"> <div class="flex space-x-1 py-5 font-bold">
<div class="title">{$t('application.application')}</div> <div class="title">{$t('application.application')}</div>

View File

@ -46,14 +46,14 @@ textarea {
} }
#svelte .custom-select-wrapper .selectContainer { #svelte .custom-select-wrapper .selectContainer {
@apply h-12 w-96 rounded border-none bg-coolgray-200 p-2 text-xs font-bold tracking-tight outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 md:text-sm; @apply h-12 w-96 rounded border-none bg-coolgray-200 p-2 px-0 text-xs tracking-tight outline-none transition duration-150 hover:bg-coolgray-500 focus:bg-coolgray-500 md:text-sm;
} }
#svelte .listContainer { #svelte .listContainer {
@apply bg-coolgray-400 text-white scrollbar-w-2 scrollbar-thumb-green-500 scrollbar-track-coolgray-200; @apply bg-coolgray-400 text-white scrollbar-w-2 scrollbar-thumb-green-500 scrollbar-track-coolgray-200;
} }
#svelte .selectedItem { #svelte .selectedItem {
@apply pl-3; @apply pl-2;
} }
#svelte .item.hover { #svelte .item.hover {