# Features
- Basic Python support

# Fixes
- Fix default start command
This commit is contained in:
Andras Bacsai 2021-05-22 15:18:58 +02:00 committed by GitHub
parent adcd68c1ab
commit c7efe899fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 225 additions and 64 deletions

View File

@ -1,7 +1,7 @@
{
"name": "coolify",
"description": "An open-source, hassle-free, self-hostable Heroku & Netlify alternative.",
"version": "1.0.13",
"version": "1.0.14",
"license": "AGPL-3.0",
"scripts": {
"dev:docker:start": "docker-compose -f docker-compose-dev.yml up -d",

View File

@ -99,6 +99,15 @@
},
build: false,
start: false
},
python: {
port: {
active: true,
number: 4000
},
build: false,
start: false,
custom: true
}
};
function selectBuildPack(event) {
@ -214,6 +223,14 @@
>
Docker
</div>
<div
class={$application.build.pack === 'python'
? 'buildpack bg-green-500'
: 'buildpack hover:border-green-500'}
on:click={selectBuildPack}
>
Python
</div>
</div>
</div>
<div class="text-2xl font-bold border-gradient w-52">General settings</div>
@ -282,68 +299,95 @@
</div>
</div>
<div
class="text-2xl font-bold w-40"
class:border-gradient={buildpacks[$application.build.pack].build}
class:text-warmGray-800={!buildpacks[$application.build.pack].build}
class="text-2xl font-bold w-40 border-gradient"
>
Commands
</div>
<div class=" max-w-2xl md:mx-auto mx-6 justify-center items-center pt-10 pb-32">
<div class="grid grid-flow-col gap-2 items-center">
<div class="grid grid-flow-row">
<label
for="installCommand"
class:text-warmGray-800={!buildpacks[$application.build.pack].build}
>Install Command <TooltipInfo
label="Command to run for installing dependencies. eg: yarn install"
{#if $application.build.pack === 'python'}
<label for="ModulePackageName"
>Module/Package Name<TooltipInfo
label="The module/package name to start (eg: the entry filename [main], without the py extension. See gunicorn.org for more details)"
/>
</label>
<input
class="mb-6"
id="ModulePackageName"
bind:value={$application.build.command.python.module}
placeholder="main"
/>
<label for="ApplicationInstance"
>Application Instance<TooltipInfo
label="The instance name (the main function name. See gunicorn.org for more details)"
/>
</label>
<input
class="mb-6"
class:bg-warmGray-900={!buildpacks[$application.build.pack].build}
class:text-warmGray-900={!buildpacks[$application.build.pack].build}
class:placeholder-warmGray-800={!buildpacks[$application.build.pack].build}
class:hover:bg-warmGray-900={!buildpacks[$application.build.pack].build}
class:cursor-not-allowed={!buildpacks[$application.build.pack].build}
id="installCommand"
bind:value={$application.build.command.installation}
placeholder="eg: yarn install"
/>
<label
for="buildCommand"
class:text-warmGray-800={!buildpacks[$application.build.pack].build}
>Build Command <TooltipInfo
label="Command to run for building your application. If empty, no build phase initiated in the deploy process."
/></label
>
<input
class="mb-6"
class:bg-warmGray-900={!buildpacks[$application.build.pack].build}
class:text-warmGray-900={!buildpacks[$application.build.pack].build}
class:placeholder-warmGray-800={!buildpacks[$application.build.pack].build}
class:hover:bg-warmGray-900={!buildpacks[$application.build.pack].build}
class:cursor-not-allowed={!buildpacks[$application.build.pack].build}
id="buildCommand"
bind:value={$application.build.command.build}
placeholder="eg: yarn build"
/>
<label
for="startCommand"
class:text-warmGray-800={!buildpacks[$application.build.pack].start}
>Start Command <TooltipInfo label="Command to start the application. eg: yarn start" /></label
>
<input
class="mb-6"
class:bg-warmGray-900={!buildpacks[$application.build.pack].start}
class:text-warmGray-900={!buildpacks[$application.build.pack].start}
class:placeholder-warmGray-800={!buildpacks[$application.build.pack].start}
class:hover:bg-warmGray-900={!buildpacks[$application.build.pack].start}
class:cursor-not-allowed={!buildpacks[$application.build.pack].start}
id="startcommand"
bind:value={$application.build.command.start}
placeholder="eg: yarn start"
id="ApplicationInstance"
bind:value={$application.build.command.python.instance}
placeholder="app"
/>
{:else}
<label
for="installCommand"
class:text-warmGray-800={!buildpacks[$application.build.pack].build}
>Install Command <TooltipInfo
label="Command to run for installing dependencies. eg: yarn install"
/>
</label>
<input
class="mb-6"
class:bg-warmGray-900={!buildpacks[$application.build.pack].build}
class:text-warmGray-900={!buildpacks[$application.build.pack].build}
class:placeholder-warmGray-800={!buildpacks[$application.build.pack].build}
class:hover:bg-warmGray-900={!buildpacks[$application.build.pack].build}
class:cursor-not-allowed={!buildpacks[$application.build.pack].build}
id="installCommand"
bind:value={$application.build.command.installation}
placeholder="eg: yarn install"
/>
<label
for="buildCommand"
class:text-warmGray-800={!buildpacks[$application.build.pack].build}
>Build Command <TooltipInfo
label="Command to run for building your application. If empty, no build phase initiated in the deploy process."
/></label
>
<input
class="mb-6"
class:bg-warmGray-900={!buildpacks[$application.build.pack].build}
class:text-warmGray-900={!buildpacks[$application.build.pack].build}
class:placeholder-warmGray-800={!buildpacks[$application.build.pack].build}
class:hover:bg-warmGray-900={!buildpacks[$application.build.pack].build}
class:cursor-not-allowed={!buildpacks[$application.build.pack].build}
id="buildCommand"
bind:value={$application.build.command.build}
placeholder="eg: yarn build"
/>
<label
for="startCommand"
class:text-warmGray-800={!buildpacks[$application.build.pack].start}
>Start Command <TooltipInfo
label="Command to start the application. eg: yarn start"
/></label
>
<input
class="mb-6"
class:bg-warmGray-900={!buildpacks[$application.build.pack].start}
class:text-warmGray-900={!buildpacks[$application.build.pack].start}
class:placeholder-warmGray-800={!buildpacks[$application.build.pack].start}
class:hover:bg-warmGray-900={!buildpacks[$application.build.pack].start}
class:cursor-not-allowed={!buildpacks[$application.build.pack].start}
id="startcommand"
bind:value={$application.build.command.start}
placeholder="eg: yarn start"
/>
{/if}
</div>
</div>
</div>

View File

@ -66,7 +66,7 @@
const packageJson = dir.find((f) => f.type === 'file' && f.name === 'package.json');
const Dockerfile = dir.find((f) => f.type === 'file' && f.name === 'Dockerfile');
const CargoToml = dir.find((f) => f.type === 'file' && f.name === 'Cargo.toml');
const requirementsTXT = dir.find((f) => f.type === 'file' && f.name === 'requirements.txt');
if (packageJson) {
const { content } = await request(packageJson.git_url, $session);
const packageJsonContent = JSON.parse(atob(content));
@ -102,10 +102,13 @@
} else if (CargoToml) {
$application.build.pack = 'rust';
browser && toast.push(`Rust language detected. Default values set.`);
} else if (requirementsTXT) {
$application.build.pack = 'python'
browser && toast.push('Python language detected. Default values set.');
} else if (Dockerfile) {
$application.build.pack = 'docker';
browser && toast.push('Custom Dockerfile found. Build pack set to docker.');
}
}
} catch (error) {
// Nothing detected
}

5
src/global.d.ts vendored
View File

@ -44,6 +44,11 @@ export type Application = {
build: string | null;
installation: string;
start: string;
python: {
module?: string;
instance?: string;
}
};
container: {
name: string;

View File

@ -39,7 +39,6 @@ async function connectMongoDB() {
}
(async () => {
console.log(mongoose.connection.readyState)
if (mongoose.connection.readyState !== 1) await connectMongoDB();
try {
await mongoose.connection.db.dropCollection('logs-servers');

View File

@ -29,9 +29,12 @@ export function setDefaultConfiguration(configuration) {
configuration.build.pack === 'vuejs' ||
configuration.build.pack === 'nuxtjs' ||
configuration.build.pack === 'rust' ||
configuration.build.pack === 'nextjs'
configuration.build.pack === 'nextjs' ||
configuration.build.pack === 'nestjs'
) {
configuration.publish.port = 3000;
} else if (configuration.build.pack === 'python') {
configuration.publish.port = 4000;
} else {
configuration.publish.port = 80;
}
@ -48,6 +51,19 @@ export function setDefaultConfiguration(configuration) {
if (!configuration.build.command.installation)
configuration.build.command.installation = 'yarn install';
}
if (
configuration.build.pack === 'nodejs' ||
configuration.build.pack === 'vuejs' ||
configuration.build.pack === 'nuxtjs' ||
configuration.build.pack === 'nextjs' ||
configuration.build.pack === 'nestjs'
) {
if (!configuration.build.command.start) configuration.build.command.start = 'yarn start'
}
if (configuration.build.pack === 'python') {
if (!configuration.build.command.python.module) configuration.build.command.python.module = 'main'
if (!configuration.build.command.python.instance) configuration.build.command.python.instance = 'app'
}
configuration.build.container.baseSHA = crypto
.createHash('sha256')
@ -103,9 +119,9 @@ export async function precheckDeployment({ services, configuration }) {
// If only the configuration changed
if (
JSON.stringify(runningWithoutContainer.build) !==
JSON.stringify(configurationWithoutContainer.build) ||
JSON.stringify(configurationWithoutContainer.build) ||
JSON.stringify(runningWithoutContainer.publish) !==
JSON.stringify(configurationWithoutContainer.publish)
JSON.stringify(configurationWithoutContainer.publish)
)
configChanged = true;
// If only the image changed
@ -148,8 +164,7 @@ export async function updateServiceLabels(configuration) {
await execShellAsync(
`docker service update --label-add configuration='${JSON.stringify(
Labels
)}' --label-add com.docker.stack.image='${configuration.build.container.name}:${
configuration.build.container.tag
)}' --label-add com.docker.stack.image='${configuration.build.container.name}:${configuration.build.container.tag
}' ${ID}`
);
}

View File

@ -10,6 +10,7 @@ import nextjs from './nextjs';
import nestjs from './nestjs';
import gatsby from './gatsby';
import docker from './docker';
import python from './python';
export {
vuejs,
@ -23,5 +24,6 @@ export {
nextjs,
nestjs,
gatsby,
docker
docker,
python
};

View File

@ -8,7 +8,7 @@ const publishPHPDocker = (configuration) => {
'WORKDIR /usr/src/app',
`COPY ./${configuration.build.directory} /var/www/html`,
'EXPOSE 80',
' CMD ["apache2-foreground"]'
'CMD ["apache2-foreground"]'
].join('\n');
};

View File

@ -0,0 +1,27 @@
import { docker, streamEvents } from '$lib/api/docker';
import { promises as fs } from 'fs';
// `HEALTHCHECK --timeout=10s --start-period=10s --interval=5s CMD curl -I -s -f http://localhost:${configuration.publish.port}${configuration.publish.path} || exit 1`,
const publishPython = (configuration) => {
return [
'FROM python:3-alpine',
'WORKDIR /usr/src/app',
'RUN pip install gunicorn',
`COPY ./${configuration.build.directory}/requirements.txt ./`,
`RUN pip install --no-cache-dir -r ./${configuration.build.directory}/requirements.txt`,
`COPY ./${configuration.build.directory}/ .`,
`EXPOSE ${configuration.publish.port}`,
`CMD gunicorn -w=4 ${configuration.build.command.python.module}:${configuration.build.command.python.instance}`
].join('\n');
};
export default async function (configuration) {
await fs.writeFile(
`${configuration.general.workdir}/Dockerfile`,
publishPython(configuration)
);
const stream = await docker.engine.buildImage(
{ src: ['.'], context: configuration.general.workdir },
{ t: `${configuration.build.container.name}:${configuration.build.container.tag}` }
);
await streamEvents(stream, configuration);
}

View File

@ -27,6 +27,11 @@ const ConfigurationSchema = new Schema({
build: { type: String },
installation: { type: String },
start: { type: String },
python: {
module: { type: String },
instance: { type: String },
}
},
container: {
name: { type: String, required: true },

View File

@ -278,6 +278,57 @@
fill-rule="evenodd"
/>
</svg>
{:else if application.configuration.build.pack === 'python'}
<svg class="w-14 h-14 absolute top-0 left-0 -m-6" viewBox="0 0 128 128">
<linearGradient
id="a"
gradientUnits="userSpaceOnUse"
x1="70.252"
y1="1237.476"
x2="170.659"
y2="1151.089"
gradientTransform="matrix(.563 0 0 -.568 -29.215 707.817)"
><stop offset="0" stop-color="#5A9FD4" /><stop
offset="1"
stop-color="#306998"
/></linearGradient
><path
fill="url(#a)"
d="M63.391 1.988c-4.222.02-8.252.379-11.8 1.007-10.45 1.846-12.346 5.71-12.346 12.837v9.411h24.693v3.137h-33.961c-7.176 0-13.46 4.313-15.426 12.521-2.268 9.405-2.368 15.275 0 25.096 1.755 7.311 5.947 12.519 13.124 12.519h8.491v-11.282c0-8.151 7.051-15.34 15.426-15.34h24.665c6.866 0 12.346-5.654 12.346-12.548v-23.513c0-6.693-5.646-11.72-12.346-12.837-4.244-.706-8.645-1.027-12.866-1.008zm-13.354 7.569c2.55 0 4.634 2.117 4.634 4.721 0 2.593-2.083 4.69-4.634 4.69-2.56 0-4.633-2.097-4.633-4.69-.001-2.604 2.073-4.721 4.633-4.721z"
/><linearGradient
id="b"
gradientUnits="userSpaceOnUse"
x1="209.474"
y1="1098.811"
x2="173.62"
y2="1149.537"
gradientTransform="matrix(.563 0 0 -.568 -29.215 707.817)"
><stop offset="0" stop-color="#FFD43B" /><stop
offset="1"
stop-color="#FFE873"
/></linearGradient
><path
fill="url(#b)"
d="M91.682 28.38v10.966c0 8.5-7.208 15.655-15.426 15.655h-24.665c-6.756 0-12.346 5.783-12.346 12.549v23.515c0 6.691 5.818 10.628 12.346 12.547 7.816 2.297 15.312 2.713 24.665 0 6.216-1.801 12.346-5.423 12.346-12.547v-9.412h-24.664v-3.138h37.012c7.176 0 9.852-5.005 12.348-12.519 2.578-7.735 2.467-15.174 0-25.096-1.774-7.145-5.161-12.521-12.348-12.521h-9.268zm-13.873 59.547c2.561 0 4.634 2.097 4.634 4.692 0 2.602-2.074 4.719-4.634 4.719-2.55 0-4.633-2.117-4.633-4.719 0-2.595 2.083-4.692 4.633-4.692z"
/><radialGradient
id="c"
cx="1825.678"
cy="444.45"
r="26.743"
gradientTransform="matrix(0 -.24 -1.055 0 532.979 557.576)"
gradientUnits="userSpaceOnUse"
><stop offset="0" stop-color="#B8B8B8" stop-opacity=".498" /><stop
offset="1"
stop-color="#7F7F7F"
stop-opacity="0"
/></radialGradient
><path
opacity=".444"
fill="url(#c)"
enable-background="new"
d="M97.309 119.597c0 3.543-14.816 6.416-33.091 6.416-18.276 0-33.092-2.873-33.092-6.416 0-3.544 14.815-6.417 33.092-6.417 18.275 0 33.091 2.872 33.091 6.417z"
/>
</svg>
{/if}
<div class="flex flex-col justify-center items-center w-full">
<div class="text-base font-bold text-center w-full text-white pb-6">

View File

@ -5,7 +5,7 @@ import type {
DateTimeFormatOptions,
GithubInstallations
} from 'src/global';
import { writable, derived, readable, Writable } from 'svelte/store';
import { writable } from 'svelte/store';
export const dashboard = writable<Dashboard>({
databases: {
@ -55,7 +55,12 @@ export const application = writable<Application>({
directory: null,
command: {
build: null,
installation: null
installation: null,
start: null,
python: {
module: null,
instance: null
}
},
container: {
name: null,
@ -99,7 +104,12 @@ export const initialApplication: Application = {
directory: null,
command: {
build: null,
installation: null
installation: null,
start: null,
python: {
module: null,
instance: null
}
},
container: {
name: null,