feat: simpleDockerfile deployment

This commit is contained in:
Andras Bacsai 2022-12-01 12:58:45 +01:00
parent a129be0dbd
commit 2e56086661
21 changed files with 459 additions and 184 deletions

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Application" ADD COLUMN "simpleDockerfile" TEXT;

View File

@ -98,7 +98,7 @@ model TeamInvitation {
}
model Application {
id String @id @default(cuid())
id String @id @default(cuid())
name String
fqdn String?
repository String?
@ -124,23 +124,25 @@ model Application {
dockerComposeFile String?
dockerComposeFileLocation String?
dockerComposeConfiguration String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
destinationDockerId String?
gitSourceId String?
gitCommitHash String?
baseImage String?
baseBuildImage String?
gitSource GitSource? @relation(fields: [gitSourceId], references: [id])
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
persistentStorage ApplicationPersistentStorage[]
settings ApplicationSettings?
secrets Secret[]
teams Team[]
connectedDatabase ApplicationConnectedDatabase?
previewApplication PreviewApplication[]
dockerRegistryId String?
dockerRegistry DockerRegistry? @relation(fields: [dockerRegistryId], references: [id])
simpleDockerfile String?
persistentStorage ApplicationPersistentStorage[]
secrets Secret[]
teams Team[]
connectedDatabase ApplicationConnectedDatabase?
previewApplication PreviewApplication[]
gitSource GitSource? @relation(fields: [gitSourceId], references: [id])
destinationDocker DestinationDocker? @relation(fields: [destinationDockerId], references: [id])
dockerRegistry DockerRegistry? @relation(fields: [dockerRegistryId], references: [id])
}
model PreviewApplication {

View File

@ -3,7 +3,7 @@ import crypto from 'crypto';
import fs from 'fs/promises';
import yaml from 'js-yaml';
import { copyBaseConfigurationFiles, makeLabelForStandaloneApplication, saveBuildLog, setDefaultConfiguration } from '../lib/buildPacks/common';
import { copyBaseConfigurationFiles, makeLabelForSimpleDockerfile, makeLabelForStandaloneApplication, saveBuildLog, setDefaultConfiguration } from '../lib/buildPacks/common';
import { createDirectories, decrypt, defaultComposeConfiguration, executeDockerCmd, getDomain, prisma, decryptApplication, isDev } from '../lib/common';
import * as importers from '../lib/importers';
import * as buildpacks from '../lib/buildPacks';
@ -39,10 +39,155 @@ import * as buildpacks from '../lib/buildPacks';
actions.push(async () => {
let application = await prisma.application.findUnique({ where: { id: queueBuild.applicationId }, include: { destinationDocker: true, gitSource: { include: { githubApp: true, gitlabApp: true } }, persistentStorage: true, secrets: true, settings: true, teams: true } })
let { id: buildId, type, sourceBranch = null, pullmergeRequestId = null, previewApplicationId = null, forceRebuild, sourceRepository = null } = queueBuild
let { id: buildId, type, gitSourceId, sourceBranch = null, pullmergeRequestId = null, previewApplicationId = null, forceRebuild, sourceRepository = null } = queueBuild
application = decryptApplication(application)
if (!gitSourceId && application.simpleDockerfile) {
const {
id: applicationId,
destinationDocker,
destinationDockerId,
secrets,
port,
persistentStorage,
exposePort,
simpleDockerfile
} = application
const { workdir } = await createDirectories({ repository: applicationId, buildId });
try {
if (queueBuild.status === 'running') {
await saveBuildLog({ line: 'Building halted, restarting...', buildId, applicationId: application.id });
}
const volumes =
persistentStorage?.map((storage) => {
if (storage.oldPath) {
return `${applicationId}${storage.path.replace(/\//gi, '-').replace('-app', '')}:${storage.path}`;
}
return `${applicationId}${storage.path.replace(/\//gi, '-')}:${storage.path}`;
}) || [];
if (destinationDockerId) {
await prisma.build.update({ where: { id: buildId }, data: { status: 'running' } });
try {
await executeDockerCmd({
dockerId: destinationDockerId,
command: `docker ps -a --filter 'label=com.docker.compose.service=${applicationId}' --format {{.ID}}|xargs -r -n 1 docker stop -t 0`
})
await executeDockerCmd({
dockerId: destinationDockerId,
command: `docker ps -a --filter 'label=com.docker.compose.service=${applicationId}' --format {{.ID}}|xargs -r -n 1 docker rm --force`
})
} catch (error) {
//
}
const envs = [
`PORT=${port}`
];
if (secrets.length > 0) {
secrets.forEach((secret) => {
if (pullmergeRequestId) {
const isSecretFound = secrets.filter(s => s.name === secret.name && s.isPRMRSecret)
if (isSecretFound.length > 0) {
envs.push(`${secret.name}=${isSecretFound[0].value}`);
} else {
envs.push(`${secret.name}=${secret.value}`);
}
} else {
if (!secret.isPRMRSecret) {
envs.push(`${secret.name}=${secret.value}`);
}
}
});
}
await fs.writeFile(`${workdir}/.env`, envs.join('\n'));
let envFound = false;
try {
envFound = !!(await fs.stat(`${workdir}/.env`));
} catch (error) {
//
}
await fs.writeFile(`${workdir}/Dockerfile`, simpleDockerfile);
const labels = makeLabelForSimpleDockerfile({
applicationId,
type,
port: exposePort ? `${exposePort}:${port}` : port,
});
try {
await saveBuildLog({ line: 'Deployment initiated', buildId, applicationId });
const composeVolumes = volumes.map((volume) => {
return {
[`${volume.split(':')[0]}`]: {
name: volume.split(':')[0]
}
};
});
const composeFile = {
version: '3.8',
services: {
[applicationId]: {
build: {
context: workdir,
},
image: `${applicationId}:${buildId}`,
container_name: applicationId,
volumes,
labels,
env_file: envFound ? [`${workdir}/.env`] : [],
depends_on: [],
expose: [port],
...(exposePort ? { ports: [`${exposePort}:${port}`] } : {}),
...defaultComposeConfiguration(destinationDocker.network),
}
},
networks: {
[destinationDocker.network]: {
external: true
}
},
volumes: Object.assign({}, ...composeVolumes)
};
await fs.writeFile(`${workdir}/docker-compose.yml`, yaml.dump(composeFile));
await executeDockerCmd({ dockerId: destinationDocker.id, command: `docker compose --project-directory ${workdir} up -d` })
await saveBuildLog({ line: 'Deployed successfully 🎉', buildId, applicationId });
} catch (error) {
await saveBuildLog({ line: error, buildId, applicationId });
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } })
if (foundBuild) {
await prisma.build.update({
where: { id: buildId },
data: {
status: 'failed'
}
});
}
throw new Error(error);
}
await prisma.build.update({ where: { id: buildId }, data: { status: 'success' } });
}
} catch (error) {
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } })
if (foundBuild) {
await prisma.build.update({
where: { id: buildId },
data: {
status: 'failed'
}
});
}
if (error !== 1) {
await saveBuildLog({ line: error, buildId, applicationId: application.id });
}
} finally {
if (!isDev) {
await fs.rm(workdir, { recursive: true, force: true });
}
}
return;
}
const originalApplicationId = application.id
const {
id: applicationId,
@ -415,8 +560,7 @@ import * as buildpacks from '../lib/buildPacks';
});
}
}
}
catch (error) {
} catch (error) {
const foundBuild = await prisma.build.findUnique({ where: { id: buildId } })
if (foundBuild) {
await prisma.build.update({

View File

@ -675,7 +675,14 @@ export async function buildImage({
await saveBuildLog({ line: `Building production image built successful 🎉`, buildId, applicationId });
}
}
export function makeLabelForSimpleDockerfile({ applicationId, port, type }) {
return [
'coolify.managed=true',
`coolify.version=${version}`,
`coolify.applicationId=${applicationId}`,
`coolify.type=standalone-application`
];
}
export function makeLabelForStandaloneApplication({
applicationId,
fqdn,

View File

@ -1091,7 +1091,7 @@ export const createDirectories = async ({
repository: string;
buildId: string;
}): Promise<{ workdir: string; repodir: string }> => {
repository = repository.replaceAll(' ', '')
if (repository) repository = repository.replaceAll(' ', '')
const repodir = `/tmp/build-sources/${repository}/`;
const workdir = `/tmp/build-sources/${repository}/${buildId}`;
let workdirFound = false;

View File

@ -41,14 +41,13 @@ export async function checkContainer({ dockerId, container, remove = false }: {
`docker rm ${container}`
});
}
return {
found: containerFound,
status: {
isRunning,
isRestarting,
isExited
}
};
} catch (err) {

View File

@ -334,7 +334,8 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
baseDatabaseBranch,
dockerComposeFile,
dockerComposeFileLocation,
dockerComposeConfiguration
dockerComposeConfiguration,
simpleDockerfile
} = request.body
if (port) port = Number(port);
if (exposePort) {
@ -373,6 +374,7 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
dockerComposeFile,
dockerComposeFileLocation,
dockerComposeConfiguration,
simpleDockerfile,
...defaultConfiguration,
connectedDatabase: { update: { hostedDatabaseDBName: baseDatabaseBranch } }
}
@ -395,6 +397,7 @@ export async function saveApplication(request: FastifyRequest<SaveApplication>,
dockerComposeFile,
dockerComposeFileLocation,
dockerComposeConfiguration,
simpleDockerfile,
...defaultConfiguration
}
});
@ -770,22 +773,37 @@ export async function deployApplication(request: FastifyRequest<DeployApplicatio
await prisma.application.update({ where: { id }, data: { configHash } });
}
await prisma.application.update({ where: { id }, data: { updatedAt: new Date() } });
await prisma.build.create({
data: {
id: buildId,
applicationId: id,
sourceBranch: branch,
branch: application.branch,
pullmergeRequestId: pullmergeRequestId?.toString(),
forceRebuild,
destinationDockerId: application.destinationDocker?.id,
gitSourceId: application.gitSource?.id,
githubAppId: application.gitSource?.githubApp?.id,
gitlabAppId: application.gitSource?.gitlabApp?.id,
status: 'queued',
type: pullmergeRequestId ? application.gitSource?.githubApp?.id ? 'manual_pr' : 'manual_mr' : 'manual'
}
});
if (application.gitSourceId) {
await prisma.build.create({
data: {
id: buildId,
applicationId: id,
sourceBranch: branch,
branch: application.branch,
pullmergeRequestId: pullmergeRequestId?.toString(),
forceRebuild,
destinationDockerId: application.destinationDocker?.id,
gitSourceId: application.gitSource?.id,
githubAppId: application.gitSource?.githubApp?.id,
gitlabAppId: application.gitSource?.gitlabApp?.id,
status: 'queued',
type: pullmergeRequestId ? application.gitSource?.githubApp?.id ? 'manual_pr' : 'manual_mr' : 'manual'
}
});
} else {
await prisma.build.create({
data: {
id: buildId,
applicationId: id,
branch: 'latest',
forceRebuild,
destinationDockerId: application.destinationDocker?.id,
status: 'queued',
type: 'manual'
}
});
}
return {
buildId
};
@ -800,20 +818,29 @@ export async function deployApplication(request: FastifyRequest<DeployApplicatio
export async function saveApplicationSource(request: FastifyRequest<SaveApplicationSource>, reply: FastifyReply) {
try {
const { id } = request.params
const { gitSourceId, forPublic, type } = request.body
const { gitSourceId, forPublic, type, simpleDockerfile } = request.body
if (forPublic) {
const publicGit = await prisma.gitSource.findFirst({ where: { type, forPublic } });
if (gitSourceId) {
await prisma.application.update({
where: { id },
data: { gitSource: { connect: { id: gitSourceId } } }
});
} else {
const publicGit = await prisma.gitSource.findFirst({ where: { type, forPublic } });
await prisma.application.update({
where: { id },
data: { gitSource: { connect: { id: publicGit.id } } }
});
}
}
if (simpleDockerfile) {
await prisma.application.update({
where: { id },
data: { gitSource: { connect: { id: publicGit.id } } }
});
} else {
await prisma.application.update({
where: { id },
data: { gitSource: { connect: { id: gitSourceId } } }
data: { simpleDockerfile }
});
}
return reply.code(201).send()
} catch ({ status, message }) {
return errorHandler({ status, message })
@ -916,11 +943,11 @@ export async function getBuildPack(request) {
const teamId = request.user?.teamId;
const application: any = await getApplicationFromDB(id, teamId);
return {
type: application.gitSource.type,
type: application.gitSource?.type || 'dockerRegistry',
projectId: application.projectId,
repository: application.repository,
branch: application.branch,
apiUrl: application.gitSource.apiUrl,
apiUrl: application.gitSource?.apiUrl || null,
isPublicRepository: application.settings.isPublicRepository
}
} catch ({ status, message }) {

View File

@ -25,7 +25,8 @@ export interface SaveApplication extends OnlyId {
baseDatabaseBranch: string,
dockerComposeFile: string,
dockerComposeFileLocation: string,
dockerComposeConfiguration: string
dockerComposeConfiguration: string,
simpleDockerfile: string
}
}
export interface SaveApplicationSettings extends OnlyId {
@ -56,7 +57,7 @@ export interface GetImages {
Body: { buildPack: string, deploymentType: string }
}
export interface SaveApplicationSource extends OnlyId {
Body: { gitSourceId?: string | null, forPublic?: boolean, type?: string }
Body: { gitSourceId?: string | null, forPublic?: boolean, type?: string, simpleDockerfile?: string }
}
export interface CheckRepository extends OnlyId {
Querystring: { repository: string, branch: string }

View File

@ -190,7 +190,7 @@ export async function showDashboard(request: FastifyRequest) {
let foundUnconfiguredApplication = false;
for (const application of applications) {
if (!application.buildPack || !application.destinationDockerId || !application.branch || (!application.settings?.isBot && !application?.fqdn) && application.buildPack !== "compose") {
if (((!application.buildPack || !application.branch) && !application.simpleDockerfile) || !application.destinationDockerId || (!application.settings?.isBot && !application?.fqdn) && application.buildPack !== "compose") {
foundUnconfiguredApplication = true
}
}

View File

@ -42,4 +42,6 @@
<Icons.Heroku {isAbsolute} />
{:else if application.buildPack?.toLowerCase() === 'compose'}
<Icons.Compose {isAbsolute} />
{:else if application.simpleDockerfile}
<Icons.Docker {isAbsolute} />
{/if}

View File

@ -19,14 +19,14 @@ export async function refreshStatus(list: Array<any>) {
}
export async function getStatus(resource: any, force: boolean = false) {
const { id, buildPack, dualCerts, engine } = resource;
const { id, buildPack, dualCerts, engine, simpleDockerfile } = resource;
let newStatus = 'stopped';
// Already set and we're not forcing
if (getStore(containerStatus)[id] && !force) return getStore(containerStatus)[id];
try {
if (buildPack) { // Application
if (buildPack || simpleDockerfile) { // Application
const response = await get(`/applications/${id}/status`);
newStatus = parseApplicationsResponse(response);
} else if (typeof dualCerts !== 'undefined') { // Service

View File

@ -57,14 +57,15 @@ export const appSession: Writable<AppSession> = writable({
export const disabledButton: Writable<boolean> = writable(false);
export const isDeploymentEnabled: Writable<boolean> = writable(false);
export function checkIfDeploymentEnabledApplications(isAdmin: boolean, application: any) {
return (
return !!(
isAdmin &&
(application.buildPack === 'compose') ||
(application.fqdn || application.settings.isBot) &&
application.gitSource &&
((application.gitSource &&
application.repository &&
application.destinationDocker &&
application.buildPack
application.buildPack) || application.simpleDockerfile) &&
application.destinationDocker
);
}
export function checkIfDeploymentEnabledServices(isAdmin: boolean, service: any) {

View File

@ -218,28 +218,30 @@
<li class="menu-title">
<span>Advanced</span>
</li>
<li
class="rounded"
class:bg-coollabs={$page.url.pathname === `/applications/${$page.params.id}/revert`}
>
<a href={`/applications/${$page.params.id}/revert`} class="no-underline w-full">
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M20 5v14l-12 -7z" />
<line x1="4" y1="5" x2="4" y2="19" />
</svg>
Revert</a
{#if !application.simpleDockerfile}
<li
class="rounded"
class:bg-coollabs={$page.url.pathname === `/applications/${$page.params.id}/revert`}
>
</li>
<a href={`/applications/${$page.params.id}/revert`} class="no-underline w-full">
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M20 5v14l-12 -7z" />
<line x1="4" y1="5" x2="4" y2="19" />
</svg>
Revert</a
>
</li>
{/if}
<li
class="rounded"
class:text-stone-600={$status.application.overallStatus !== 'healthy'}
@ -265,7 +267,7 @@
</svg>Monitoring</a
>
</li>
{#if !application.settings.isBot}
{#if !application.settings.isBot && !application.simpleDockerfile}
<li
class="rounded"
class:bg-coollabs={$page.url.pathname === `/applications/${$page.params.id}/previews`}

View File

@ -2,8 +2,14 @@
import type { Load } from '@sveltejs/kit';
function checkConfiguration(application: any): string | null {
let configurationPhase = null;
if (!application.gitSourceId) {
configurationPhase = 'source';
if (!application.gitSourceId && !application.simpleDockerfile) {
return (configurationPhase = 'source');
}
if (application.simpleDockerfile) {
if (!application.destinationDockerId) {
configurationPhase = 'destination';
}
return configurationPhase;
} else if (!application.repository && !application.branch) {
configurationPhase = 'repository';
} else if (!application.destinationDockerId) {

View File

@ -9,6 +9,12 @@
redirect: `/applications/${params.id}`
};
}
if (application.simpleDockerfile) {
return {
status: 302,
redirect: `/applications/${params.id}`
};
}
const response = await get(`/applications/${params.id}/configuration/buildpack`);
return {
props: {
@ -47,7 +53,7 @@
const { id } = $page.params;
let htmlUrl = application.gitSource.htmlUrl;
let htmlUrl = application.gitSource?.htmlUrl || null;
let scanning: boolean = true;
let foundConfig: any = null;

View File

@ -25,6 +25,8 @@
</script>
<script lang="ts">
export let sources: any;
import { page } from '$app/stores';
import { goto } from '$app/navigation';
import { get, post } from '$lib/api';
@ -33,11 +35,12 @@
import { appSession } from '$lib/store';
import PublicRepository from './_PublicRepository.svelte';
import DocLink from '$lib/components/DocLink.svelte';
import Beta from '$lib/components/Beta.svelte';
const { id } = $page.params;
const from = $page.url.searchParams.get('from');
let simpleDockerfile: any = null;
export let sources: any;
const filteredSources = sources.filter(
(source: any) =>
(source.type === 'github' && source.githubAppId && source.githubApp.installationId) ||
@ -61,18 +64,20 @@
return errorNotification(error);
}
}
async function newSource() {
const { id } = await post('/sources/new', {});
return await goto(`/sources/${id}`, { replaceState: true });
async function handleDockerImage() {
try {
await post(`/applications/${id}/configuration/source`, { simpleDockerfile });
return await goto(from || `/applications/${id}/configuration/destination`);
} catch (error) {
return errorNotification(error);
}
}
</script>
<div class="max-w-screen-2xl mx-auto px-9">
{#if !filteredSources}
<div class="title pb-8">Git App</div>
{/if}
<div class="flex flex-wrap justify-center">
{#if !filteredSources}
<div class="flex flex-wrap justify-center">
<div class="flex-col">
<div class="pb-2 text-center font-bold">
{$t('application.configuration.no_configurable_git')}
@ -95,7 +100,13 @@
</a>
</div>
</div>
{:else}
</div>
{/if}
{#if ownSources.length > 0 || otherSources.length > 0}
<div class="title pb-8">Integrated with Git App</div>
{/if}
{#if ownSources.length > 0}
<div class="flex flex-wrap justify-center">
<div class="flex flex-col lg:flex-row lg:flex-wrap justify-center">
{#each ownSources as source}
<div class="p-2 relative">
@ -240,11 +251,25 @@
</div>
{/each}
</div>
{/if}
</div>
</div>
{/if}
<div class="flex flex-row items-center">
<div class="title py-4 pr-4">Public Repository</div>
<div class="title py-4 pr-4">Public Repository from Git</div>
<DocLink url="https://docs.coollabs.io/coolify/applications/#public-repository" />
</div>
<PublicRepository />
<div class="flex flex-row items-center pt-10">
<div class="title py-4 pr-4">Simple Dockerfile <Beta /></div>
<DocLink url="https://docs.coollabs.io/coolify/applications/#dockerfile" />
</div>
<div class="mx-auto max-w-screen-2xl">
<form class="flex flex-col" on:submit|preventDefault={handleDockerImage}>
<div class="flex flex-col space-y-2 w-full">
<div class="flex flex-row space-x-2">
<textarea required class="w-full" rows="10" bind:value={simpleDockerfile} />
<button class="btn btn-primary" type="submit">Deploy Dockerfile</button>
</div>
</div>
</form>
</div>
</div>

View File

@ -124,7 +124,7 @@
description={$t('application.enable_auto_deploy_webhooks')}
/>
</div>
{#if !application.settings.isBot}
{#if !application.settings.isBot && !application.simpleDockerfile}
<div class="grid grid-cols-2 items-center">
<Setting
id="previews"

View File

@ -66,6 +66,7 @@
save: false,
reloadCompose: false
};
let isSimpleDockerfile = !!application.simpleDockerfile;
let fqdnEl: any = null;
let forceSave = false;
let isPublicRepository = application.settings?.isPublicRepository;
@ -268,7 +269,7 @@
}
}
}
await saveForm(id, application,baseDatabaseBranch, dockerComposeConfiguration);
await saveForm(id, application, baseDatabaseBranch, dockerComposeConfiguration);
setLocation(application, settings);
$isDeploymentEnabled = checkIfDeploymentEnabledApplications($appSession.isAdmin, application);
@ -492,79 +493,84 @@
<label for="name">{$t('forms.name')}</label>
<input name="name" id="name" class="w-full" bind:value={application.name} required />
</div>
<div class="grid grid-cols-2 items-center">
<label for="gitSource">{$t('application.git_source')}</label>
{#if isDisabled || application.settings.isPublicRepository}
<input
disabled={isDisabled || application.settings.isPublicRepository}
class="w-full"
value={application.gitSource?.name}
/>
{:else}
<a
href={`/applications/${id}/configuration/source?from=/applications/${id}`}
class="no-underline"
><input
{#if !isSimpleDockerfile}
<div class="grid grid-cols-2 items-center">
<label for="gitSource">{$t('application.git_source')}</label>
{#if isDisabled || application.settings.isPublicRepository}
<input
disabled={isDisabled || application.settings.isPublicRepository}
class="w-full"
value={application.gitSource?.name}
id="gitSource"
class="cursor-pointer hover:bg-coolgray-500 w-full"
/></a
>
{/if}
</div>
<div class="grid grid-cols-2 items-center">
<label for="repository">Git commit</label>
<div class="flex gap-2">
<input
id="commit"
name="commit"
class="w-full"
disabled={isDisabled}
placeholder="default: latest commit"
bind:value={application.gitCommitHash}
/>
<a
href="{application.gitSource
.htmlUrl}/{application.repository}/commits/{application.branch}"
target="_blank noreferrer"
class="btn btn-primary text-xs"
>Commits<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 24 24"
stroke-width="3"
stroke="currentColor"
class="w-3 h-3 text-white ml-2"
/>
{:else}
<a
href={`/applications/${id}/configuration/source?from=/applications/${id}`}
class="no-underline"
><input
value={application.gitSource?.name}
id="gitSource"
class="cursor-pointer hover:bg-coolgray-500 w-full"
/></a
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M4.5 19.5l15-15m0 0H8.25m11.25 0v11.25"
/>
</svg></a
>
{/if}
</div>
</div>
<div class="grid grid-cols-2 items-center">
<label for="repository">{$t('application.git_repository')}</label>
{#if isDisabled || application.settings.isPublicRepository}
<input
class="w-full"
disabled={isDisabled || application.settings.isPublicRepository}
value="{application.repository}/{application.branch}"
/>
{:else}
<a
href={`/applications/${id}/configuration/repository?from=/applications/${id}&to=/applications/${id}/configuration/buildpack`}
class="no-underline"
><input
<div class="grid grid-cols-2 items-center">
<label for="repository">Git commit</label>
<div class="flex gap-2">
<input
id="commit"
name="commit"
class="w-full"
disabled={isDisabled}
placeholder="default: latest commit"
bind:value={application.gitCommitHash}
/>
<a
href="{application.gitSource
.htmlUrl}/{application.repository}/commits/{application.branch}"
target="_blank noreferrer"
class="btn btn-primary text-xs"
>Commits<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 24 24"
stroke-width="3"
stroke="currentColor"
class="w-3 h-3 text-white ml-2"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M4.5 19.5l15-15m0 0H8.25m11.25 0v11.25"
/>
</svg></a
>
</div>
</div>
<div class="grid grid-cols-2 items-center">
<label for="repository">{$t('application.git_repository')}</label>
{#if isDisabled || application.settings.isPublicRepository}
<input
class="w-full"
disabled={isDisabled || application.settings.isPublicRepository}
value="{application.repository}/{application.branch}"
id="repository"
class="cursor-pointer hover:bg-coolgray-500 w-full"
/></a
>
{/if}
</div>
/>
{:else}
<a
href={`/applications/${id}/configuration/repository?from=/applications/${id}&to=/applications/${id}/configuration/buildpack`}
class="no-underline"
><input
value="{application.repository}/{application.branch}"
id="repository"
class="cursor-pointer hover:bg-coolgray-500 w-full"
/></a
>
{/if}
</div>
{:else}
{/if}
<div class="grid grid-cols-2 items-center">
<label for="registry">Docker Registry</label>
{#if isDisabled}
@ -586,23 +592,29 @@
>
{/if}
</div>
<div class="grid grid-cols-2 items-center">
<label for="buildPack">{$t('application.build_pack')} </label>
{#if isDisabled}
<input class="capitalize w-full" disabled={isDisabled} value={application.buildPack} />
{:else}
<a
href={`/applications/${id}/configuration/buildpack?from=/applications/${id}`}
class="no-underline"
>
{#if !isSimpleDockerfile}
<div class="grid grid-cols-2 items-center">
<label for="buildPack">{$t('application.build_pack')} </label>
{#if isDisabled}
<input
class="capitalize w-full"
disabled={isDisabled}
value={application.buildPack}
id="buildPack"
class="cursor-pointer hover:bg-coolgray-500 capitalize w-full"
/></a
>
{/if}
</div>
/>
{:else}
<a
href={`/applications/${id}/configuration/buildpack?from=/applications/${id}`}
class="no-underline"
>
<input
value={application.buildPack}
id="buildPack"
class="cursor-pointer hover:bg-coolgray-500 capitalize w-full"
/></a
>
{/if}
</div>
{/if}
<div class="grid grid-cols-2 items-center">
<label for="destination">{$t('application.destination')}</label>
<div class="no-underline">
@ -712,7 +724,44 @@
{/if}
{/if}
</div>
{#if application.buildPack !== 'compose'}
{#if isSimpleDockerfile}
<div class="title font-bold pb-3 pt-10 border-b border-coolgray-500 mb-6">
Configuration
</div>
<div class="grid grid-flow-row gap-2 px-4 pr-5">
<div class="grid grid-cols-2 items-center pt-4">
<label for="simpleDockerfile">Dockerfile</label>
<div class="flex gap-2">
<textarea
rows=10
id="simpleDockerfile"
name="simpleDockerfile"
class="w-full"
disabled={isDisabled}
bind:value={application.simpleDockerfile}
/>
</div>
</div>
<div class="grid grid-cols-2 items-center">
<label for="port"
>{$t('forms.port')}
<Explainer
explanation={'The port your application listens inside the docker container.'}
/></label
>
<input
class="w-full"
disabled={isDisabled}
readonly={!$appSession.isAdmin}
name="port"
id="port"
bind:value={application.port}
placeholder="{$t('forms.default')}: 3000"
/>
</div>
</div>
{:else if application.buildPack !== 'compose'}
<div class="title font-bold pb-3 pt-10 border-b border-coolgray-500 mb-6">
Configuration
</div>

View File

@ -20,6 +20,7 @@
<script lang="ts">
export let secrets: any;
export let application: any;
export let previewSecrets: any;
import pLimit from 'p-limit';
import { page } from '$app/stores';
@ -28,7 +29,6 @@
import Secret from './_Secret.svelte';
import PreviewSecret from './_PreviewSecret.svelte';
import { errorNotification } from '$lib/common';
import { t } from '$lib/translations';
import Explainer from '$lib/components/Explainer.svelte';
const limit = pLimit(1);
@ -110,6 +110,7 @@
<div class="lg:pt-0 pt-10">
<Secret on:refresh={refreshSecrets} length={secrets.length} isNewSecret />
</div>
{#if !application.settings.isBot && !application.simpleDockerfile}
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2">
<div class="title font-bold pb-3 pt-8">
Preview Secrets <Explainer
@ -133,6 +134,7 @@
{:else}
Add secrets first to see Preview Secrets.
{/if}
{/if}
</div>
<form on:submit|preventDefault={getValues} class="mb-12 w-full">
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2 pt-10">

View File

@ -86,7 +86,7 @@
<Storage on:refresh={refreshStorage} {storage} />
{/key}
{/each}
<div class="title pt-10">
<div class="Preview Secrets" class:pt-10={predefinedVolumes.length > 0}>
Add New Volume <Explainer
position="dropdown-bottom"
explanation={$t('application.storage.persistent_storage_explainer')}

View File

@ -29,7 +29,7 @@
export let settings: any;
export let gitSources: any;
export let destinations: any;
let filtered: any = setInitials();
import { get, post } from '$lib/api';
import { t } from '$lib/translations';
@ -151,7 +151,7 @@
}
async function getStatus(resources: any, force: boolean = false) {
const { id, buildPack, dualCerts, type } = resources;
const { id, buildPack, dualCerts, type, simpleDockerfile } = resources;
if (buildPack && applications.length + filtered.otherApplications.length > 10 && !force) {
noInitialStatus.applications = true;
return;
@ -172,7 +172,7 @@
numberOfGetStatus++;
let isRunning = false;
let isDegraded = false;
if (buildPack) {
if (buildPack || simpleDockerfile) {
const response = await get(`/applications/${id}/status`);
if (response.length === 0) {
isRunning = false;