feat: Use compose instead of normal docker cmd

This commit is contained in:
Andras Bacsai 2022-03-23 10:25:32 +01:00
parent f0ab3750bd
commit 2bf73109b2
13 changed files with 131 additions and 191 deletions

View File

@ -30,6 +30,7 @@
"@sveltejs/kit": "1.0.0-next.288",
"@types/bcrypt": "5.0.0",
"@types/js-cookie": "3.0.1",
"@types/js-yaml": "^4.0.5",
"@types/node": "17.0.21",
"@types/node-forge": "1.0.0",
"@typescript-eslint/eslint-plugin": "4.31.1",

9
pnpm-lock.yaml generated
View File

@ -9,6 +9,7 @@ specifiers:
'@sveltejs/kit': 1.0.0-next.288
'@types/bcrypt': 5.0.0
'@types/js-cookie': 3.0.1
'@types/js-yaml': ^4.0.5
'@types/node': 17.0.21
'@types/node-forge': 1.0.0
'@typescript-eslint/eslint-plugin': 4.31.1
@ -84,6 +85,7 @@ devDependencies:
'@sveltejs/kit': 1.0.0-next.288_svelte@3.46.4
'@types/bcrypt': 5.0.0
'@types/js-cookie': 3.0.1
'@types/js-yaml': 4.0.5
'@types/node': 17.0.21
'@types/node-forge': 1.0.0
'@typescript-eslint/eslint-plugin': 4.31.1_386b67ad67ef29c6a0ccaf3e9b60f945
@ -537,6 +539,13 @@ packages:
}
dev: true
/@types/js-yaml/4.0.5:
resolution:
{
integrity: sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==
}
dev: true
/@types/json-schema/7.0.9:
resolution:
{

1
src/app.d.ts vendored
View File

@ -9,6 +9,7 @@ declare namespace App {
interface Session extends SessionData {}
interface Stuff {
application: any;
isRunning: boolean;
}
}

View File

@ -29,10 +29,10 @@ export function makeLabelForStandaloneApplication({
fqdn = `${protocol}://${pullmergeRequestId}.${domain}`;
}
return [
'--label coolify.managed=true',
`--label coolify.version=${version}`,
`--label coolify.type=standalone-application`,
`--label coolify.configuration=${base64Encode(
'coolify.managed=true',
`coolify.version=${version}`,
`coolify.type=standalone-application`,
`coolify.configuration=${base64Encode(
JSON.stringify({
applicationId,
fqdn,

View File

@ -6,13 +6,6 @@ const createDockerfile = async (data, image): Promise<void> => {
const Dockerfile: Array<string> = [];
Dockerfile.push(`FROM ${image}`);
Dockerfile.push(`LABEL coolify.image=true`);
if (data.phpModules?.length > 0) {
Dockerfile.push(
`ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/`
);
Dockerfile.push(`RUN chmod +x /usr/local/bin/install-php-extensions`);
Dockerfile.push(`RUN /usr/local/bin/install-php-extensions ${data.phpModules.join(' ')}`);
}
Dockerfile.push('WORKDIR /app');
Dockerfile.push(`COPY .${baseDirectory || ''} /app`);
Dockerfile.push(`COPY /.htaccess .`);

View File

@ -9,7 +9,16 @@ export const dateOptions: DateTimeFormatOptions = {
hour12: false
};
export const staticDeployments = ['react', 'vuejs', 'static', 'svelte', 'gatsby', 'php'];
export const staticDeployments = [
'react',
'vuejs',
'static',
'svelte',
'gatsby',
'php',
'astro',
'eleventy'
];
export const notNodeDeployments = ['php', 'docker', 'rust'];
export function getDomain(domain) {

View File

@ -66,6 +66,7 @@ export async function removeApplication({ id, teamId }) {
await prisma.buildLog.deleteMany({ where: { applicationId: id } });
await prisma.build.deleteMany({ where: { applicationId: id } });
await prisma.secret.deleteMany({ where: { applicationId: id } });
await prisma.applicationPersistentStorage.deleteMany({ where: { applicationId: id } });
await prisma.application.deleteMany({ where: { id, teams: { some: { id: teamId } } } });
}
@ -157,9 +158,6 @@ export async function getApplication({ id, teamId }) {
return s;
});
}
if (body?.phpModules) {
body.phpModules = body.phpModules.split(',');
}
return { ...body };
}
@ -215,8 +213,7 @@ export async function configureApplication({
buildCommand,
startCommand,
baseDirectory,
publishDirectory,
phpModules
publishDirectory
}) {
return await prisma.application.update({
where: { id },
@ -229,8 +226,7 @@ export async function configureApplication({
startCommand,
baseDirectory,
publishDirectory,
name,
phpModules
name
}
});
}

View File

@ -19,6 +19,7 @@ import {
makeLabelForStandaloneApplication,
setDefaultConfiguration
} from '$lib/buildPacks/common';
import yaml from 'js-yaml';
export default async function (job) {
/*
@ -71,7 +72,7 @@ export default async function (job) {
let volumes =
persistentStorage?.map((storage) => {
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${
type !== 'docker' ? '/app/' : ''
type !== 'docker' ? '/app' : ''
}${storage.path}`;
}) || [];
// Previews, we need to get the source branch and set subdomain
@ -261,23 +262,53 @@ export default async function (job) {
}
try {
saveBuildLog({ line: 'Deployment started.', buildId, applicationId });
for await (const volume of volumes) {
const id = volume.split(':')[0];
try {
await asyncExecShell(`DOCKER_HOST=${host} docker volume inspect ${id}`);
} catch (error) {
await asyncExecShell(`DOCKER_HOST=${host} docker volume create ${id}`);
}
}
volumes = volumes.map((volume) => `-v ${volume} `).join();
const { stderr } = await asyncExecShell(
`DOCKER_HOST=${host} docker run ${envFound && `--env-file=${workdir}/.env`} ${labels.join(
' '
)} --name ${imageId} --network ${docker.network} --restart always ${
volumes.length > 0 ? volumes : ''
} -d ${applicationId}:${tag}`
// for await (const volume of volumes) {
// const id = volume.split(':')[0];
// try {
// await asyncExecShell(`DOCKER_HOST=${host} docker volume inspect ${id}`);
// } catch (error) {
// await asyncExecShell(`DOCKER_HOST=${host} docker volume create ${id}`);
// }
// }
const composeVolumes = volumes.map((volume) => {
return {
[`${volume.split(':')[0]}`]: {
name: volume.split(':')[0]
}
};
});
const compose = {
version: '3.8',
services: {
[imageId]: {
image: `${applicationId}:${tag}`,
container_name: imageId,
volumes,
env_file: envFound ? [`${workdir}/.env`] : [],
networks: [docker.network],
labels: labels,
depends_on: [],
restart: 'always'
}
},
networks: {
[docker.network]: {
external: true
}
},
volumes: Object.assign({}, ...composeVolumes)
};
await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(compose));
await asyncExecShell(
`DOCKER_HOST=${host} docker compose --project-directory ${workdir} up -d`
);
if (stderr) console.log(stderr);
// const { stderr } = await asyncExecShell(
// `DOCKER_HOST=${host} docker run ${envFound && `--env-file=${workdir}/.env`} ${labels.join(
// ' '
// )} --name ${imageId} --network ${docker.network} --restart always ${volumes.length > 0 ? volumes : ''
// } -d ${applicationId}:${tag}`
// );
saveBuildLog({ line: 'Deployment successful!', buildId, applicationId });
} catch (error) {
saveBuildLog({ line: error, buildId, applicationId });

View File

@ -52,8 +52,7 @@ export const post: RequestHandler = async (event) => {
buildCommand,
startCommand,
baseDirectory,
publishDirectory,
phpModules
publishDirectory
} = await event.request.json();
if (port) port = Number(port);
@ -69,8 +68,7 @@ export const post: RequestHandler = async (event) => {
buildCommand,
startCommand,
baseDirectory,
publishDirectory,
phpModules
publishDirectory
});
return { status: 201 };
} catch (error) {

View File

@ -47,125 +47,7 @@
import { post } from '$lib/api';
import cuid from 'cuid';
import { browser } from '$app/env';
import Select from 'svelte-select';
const { id } = $page.params;
let collection = [
'amqp',
'apcu',
'apcu_bc',
'ast',
'bcmath',
'blackfire',
'bz2',
'calendar',
'cmark',
'csv',
'dba',
'decimal',
'ds',
'enchant',
'ev',
'event',
'excimer',
'exif',
'ffi',
'gd',
'gearman',
'geoip',
'geospatial',
'gettext',
'gmagick',
'gmp',
'gnupg',
'grpc',
'http',
'igbinary',
'imagick',
'imap',
'inotify',
'interbase',
'intl',
'ioncube_loader',
'jsmin',
'json_post',
'ldap',
'lzf',
'mailparse',
'maxminddb',
'mcrypt',
'memcache',
'memcached',
'mongo',
'mongodb',
'mosquitto',
'msgpack',
'mssql',
'mysqli',
'oauth',
'oci8',
'odbc',
'opcache',
'opencensus',
'openswoole',
'parallel',
'pcntl',
'pcov',
'pdo_dblib',
'pdo_firebird',
'pdo_mysql',
'pdo_oci',
'pdo_odbc',
'pdo_pgsql',
'pdo_sqlsrv',
'pgsql',
'propro',
'protobuf',
'pspell',
'pthreads',
'raphf',
'rdkafka',
'recode',
'redis',
'seaslog',
'shmop',
'smbclient',
'snmp',
'snuffleupagus',
'soap',
'sockets',
'solr',
'sourceguardian',
'spx',
'sqlsrv',
'ssh2',
'stomp',
'swoole',
'sybase_ct',
'sysvmsg',
'sysvsem',
'sysvshm',
'tensor',
'tidy',
'timezonedb',
'uopz',
'uploadprogress',
'uuid',
'vips',
'wddx',
'xdebug',
'xhprof',
'xlswriter',
'xmldiff',
'xmlrpc',
'xsl',
'yac',
'yaml',
'yar',
'zephir_parser',
'zip',
'zookeeper',
'zstd'
];
let domainEl: HTMLInputElement;
@ -225,9 +107,8 @@
async function handleSubmit() {
loading = true;
try {
const tempPhpModules = application.phpModules?.map((module) => module.value).toString() || '';
await post(`/applications/${id}/check.json`, { fqdn: application.fqdn, forceSave });
await post(`/applications/${id}.json`, { ...application, phpModules: tempPhpModules });
await post(`/applications/${id}.json`, { ...application });
return window.location.reload();
} catch ({ error }) {
if (error.startsWith('DNS not set')) {
@ -481,19 +362,6 @@
/>
</div>
{/if}
<!-- {#if application.buildPack === 'php'}
<div class="grid grid-cols-2 items-center">
<label for="startCommand" class="text-base font-bold text-stone-100">PHP Modules</label>
<div class="svelte-select">
<Select
isMulti={true}
bind:value={application.phpModules}
items={collection}
placeholder="Select PHP modules to add..."
/>
</div>
</div>
{/if} -->
<div class="grid grid-cols-2 items-center">
<div class="flex-col">
<label for="baseDirectory" class="pt-2 text-base font-bold text-stone-100"

View File

@ -6,17 +6,31 @@
};
import { del, post } from '$lib/api';
import { page } from '$app/stores';
import { createEventDispatcher } from 'svelte';
import { errorNotification } from '$lib/form';
import { toast } from '@zerodevx/svelte-toast';
const { id } = $page.params;
async function saveStorage() {
const dispatch = createEventDispatcher();
async function saveStorage(newStorage = false) {
try {
if (!storage.path) return errorNotification('Path is required.');
storage.path = storage.path.startsWith('/') ? storage.path : `/${storage.path}`;
storage.path = storage.path.endsWith('/') ? storage.path.slice(0, -1) : storage.path;
storage.path.replace(/\/\//g, '/');
await post(`/applications/${id}/storage.json`, {
path: storage.path
path: storage.path,
storageId: storage.id,
newStorage
});
dispatch('refresh');
if (isNew) {
storage.path = null;
storage.id = null;
}
if (newStorage) toast.push('Storage saved.');
else toast.push('Storage updated.');
} catch ({ error }) {
return errorNotification(error);
}
@ -24,6 +38,8 @@
async function removeStorage() {
try {
await del(`/applications/${id}/storage.json`, { path: storage.path });
dispatch('refresh');
toast.push('Storage deleted.');
} catch ({ error }) {
return errorNotification(error);
}
@ -32,7 +48,6 @@
<td>
<input
readonly={!isNew}
bind:value={storage.path}
required
placeholder="eg: /sqlite.db"
@ -40,10 +55,19 @@
/>
</td>
<td>
<div class="flex items-center justify-center px-2">
<button class="bg-green-600 hover:bg-green-500" on:click={saveStorage}>Add</button>
</div>
<div class="flex items-center justify-center px-2">
<button class="bg-green-600 hover:bg-green-500" on:click={removeStorage}>Remove</button>
</div>
{#if isNew}
<div class="flex items-center justify-center">
<button class="bg-green-600 hover:bg-green-500" on:click={() => saveStorage(true)}>Add</button
>
</div>
{:else}
<div class="flex flex-row justify-center space-x-2">
<div class="flex items-center justify-center">
<button class="" on:click={() => saveStorage(false)}>Set</button>
</div>
<div class="flex justify-center items-end">
<button class="bg-red-600 hover:bg-red-500" on:click={removeStorage}>Remove</button>
</div>
</div>
{/if}
</td>

View File

@ -1,9 +1,7 @@
import { getTeam, getUserDetails } from '$lib/common';
import { getUserDetails } from '$lib/common';
import * as db from '$lib/database';
import { ErrorHandler } from '$lib/database';
import { dockerInstance } from '$lib/docker';
import type { RequestHandler } from '@sveltejs/kit';
import jsonwebtoken from 'jsonwebtoken';
export const get: RequestHandler = async (event) => {
const { status, body, teamId } = await getUserDetails(event, false);
@ -27,11 +25,18 @@ export const post: RequestHandler = async (event) => {
if (status === 401) return { status, body };
const { id } = event.params;
const { path } = await event.request.json();
const { path, newStorage, storageId } = await event.request.json();
try {
await db.prisma.applicationPersistentStorage.create({
data: { path, application: { connect: { id } } }
});
if (newStorage) {
await db.prisma.applicationPersistentStorage.create({
data: { path, application: { connect: { id } } }
});
} else {
await db.prisma.applicationPersistentStorage.update({
where: { id: storageId },
data: { path }
});
}
return {
status: 201
};

View File

@ -26,8 +26,13 @@
import { getDomain } from '$lib/components/common';
import { page } from '$app/stores';
import Storage from './_Storage.svelte';
import { get } from '$lib/api';
const { id } = $page.params;
async function refreshStorage() {
const data = await get(`/applications/${id}/storage.json`);
persistentStorages = [...data.persistentStorages];
}
</script>
<div class="flex space-x-1 p-6 font-bold">
@ -49,12 +54,12 @@
{#each persistentStorages as storage}
{#key storage.id}
<tr>
<Storage {storage} />
<Storage on:refresh={refreshStorage} {storage} />
</tr>
{/key}
{/each}
<tr>
<Storage isNew />
<Storage on:refresh={refreshStorage} isNew />
</tr>
</tbody>
</table>