diff --git a/package.json b/package.json index 405ace627..93e16ca89 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/Application/ActiveTab/General.svelte b/src/components/Application/ActiveTab/General.svelte index 2a0c08100..74a56676d 100644 --- a/src/components/Application/ActiveTab/General.svelte +++ b/src/components/Application/ActiveTab/General.svelte @@ -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 +
+ Python +
General settings
@@ -282,68 +299,95 @@
Commands
- + + + - - - - + {:else} + + + + + + + + {/if}
diff --git a/src/components/Application/Tabs.svelte b/src/components/Application/Tabs.svelte index a10f889ee..d81507916 100644 --- a/src/components/Application/Tabs.svelte +++ b/src/components/Application/Tabs.svelte @@ -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 } diff --git a/src/global.d.ts b/src/global.d.ts index aa2cf612c..e7505ffa0 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -44,6 +44,11 @@ export type Application = { build: string | null; installation: string; start: string; + python: { + module?: string; + instance?: string; + } + }; container: { name: string; diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 975b64710..31edcccee 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -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'); diff --git a/src/lib/api/applications/configuration.ts b/src/lib/api/applications/configuration.ts index 0d7061944..b19a53f30 100644 --- a/src/lib/api/applications/configuration.ts +++ b/src/lib/api/applications/configuration.ts @@ -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}` ); } diff --git a/src/lib/api/applications/packs/index.ts b/src/lib/api/applications/packs/index.ts index cad73a185..5f7c8c9c8 100644 --- a/src/lib/api/applications/packs/index.ts +++ b/src/lib/api/applications/packs/index.ts @@ -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 }; diff --git a/src/lib/api/applications/packs/php/index.ts b/src/lib/api/applications/packs/php/index.ts index 5bd55bb81..b576018c6 100644 --- a/src/lib/api/applications/packs/php/index.ts +++ b/src/lib/api/applications/packs/php/index.ts @@ -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'); }; diff --git a/src/lib/api/applications/packs/python/index.ts b/src/lib/api/applications/packs/python/index.ts new file mode 100644 index 000000000..ffe64f5c4 --- /dev/null +++ b/src/lib/api/applications/packs/python/index.ts @@ -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); +} diff --git a/src/models/Configuration.ts b/src/models/Configuration.ts index 9fbe1685f..8d071918f 100644 --- a/src/models/Configuration.ts +++ b/src/models/Configuration.ts @@ -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 }, diff --git a/src/routes/dashboard/applications.svelte b/src/routes/dashboard/applications.svelte index 4ccc61a4d..d82265126 100644 --- a/src/routes/dashboard/applications.svelte +++ b/src/routes/dashboard/applications.svelte @@ -278,6 +278,57 @@ fill-rule="evenodd" /> + {:else if application.configuration.build.pack === 'python'} + + + {/if}
diff --git a/src/store/index.ts b/src/store/index.ts index 407e00852..dedb79617 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -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({ databases: { @@ -55,7 +55,12 @@ export const application = writable({ 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,