feat: deploy specific commit for apps
feat: keep number of images locally to revert quickly
This commit is contained in:
@@ -0,0 +1,35 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Application" ADD COLUMN "gitCommitHash" TEXT;
|
||||||
|
|
||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Setting" (
|
||||||
|
"id" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"fqdn" TEXT,
|
||||||
|
"dualCerts" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"minPort" INTEGER NOT NULL DEFAULT 9000,
|
||||||
|
"maxPort" INTEGER NOT NULL DEFAULT 9100,
|
||||||
|
"DNSServers" TEXT NOT NULL DEFAULT '1.1.1.1,8.8.8.8',
|
||||||
|
"ipv4" TEXT,
|
||||||
|
"ipv6" TEXT,
|
||||||
|
"arch" TEXT,
|
||||||
|
"concurrentBuilds" INTEGER NOT NULL DEFAULT 1,
|
||||||
|
"applicationStoragePathMigrationFinished" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"numberOfDockerImagesKeptLocally" INTEGER NOT NULL DEFAULT 3,
|
||||||
|
"proxyDefaultRedirect" TEXT,
|
||||||
|
"doNotTrack" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"sentryDSN" TEXT,
|
||||||
|
"isAPIDebuggingEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isRegistrationEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isAutoUpdateEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"isDNSCheckEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"isTraefikUsed" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Setting" ("DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "sentryDSN", "updatedAt") SELECT "DNSServers", "applicationStoragePathMigrationFinished", "arch", "concurrentBuilds", "createdAt", "doNotTrack", "dualCerts", "fqdn", "id", "ipv4", "ipv6", "isAPIDebuggingEnabled", "isAutoUpdateEnabled", "isDNSCheckEnabled", "isRegistrationEnabled", "isTraefikUsed", "maxPort", "minPort", "proxyDefaultRedirect", "sentryDSN", "updatedAt" FROM "Setting";
|
||||||
|
DROP TABLE "Setting";
|
||||||
|
ALTER TABLE "new_Setting" RENAME TO "Setting";
|
||||||
|
CREATE UNIQUE INDEX "Setting_fqdn_key" ON "Setting"("fqdn");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
@@ -30,6 +30,7 @@ model Setting {
|
|||||||
arch String?
|
arch String?
|
||||||
concurrentBuilds Int @default(1)
|
concurrentBuilds Int @default(1)
|
||||||
applicationStoragePathMigrationFinished Boolean @default(false)
|
applicationStoragePathMigrationFinished Boolean @default(false)
|
||||||
|
numberOfDockerImagesKeptLocally Int @default(3)
|
||||||
proxyDefaultRedirect String?
|
proxyDefaultRedirect String?
|
||||||
doNotTrack Boolean @default(false)
|
doNotTrack Boolean @default(false)
|
||||||
sentryDSN String?
|
sentryDSN String?
|
||||||
|
@@ -65,6 +65,7 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
baseImage,
|
baseImage,
|
||||||
baseBuildImage,
|
baseBuildImage,
|
||||||
deploymentType,
|
deploymentType,
|
||||||
|
gitCommitHash,
|
||||||
} = application
|
} = application
|
||||||
|
|
||||||
let {
|
let {
|
||||||
@@ -169,7 +170,7 @@ import * as buildpacks from '../lib/buildPacks';
|
|||||||
githubAppId: gitSource.githubApp?.id,
|
githubAppId: gitSource.githubApp?.id,
|
||||||
gitlabAppId: gitSource.gitlabApp?.id,
|
gitlabAppId: gitSource.gitlabApp?.id,
|
||||||
customPort: gitSource.customPort,
|
customPort: gitSource.customPort,
|
||||||
gitCommitHash: gitCommitHash,
|
gitCommitHash,
|
||||||
configuration,
|
configuration,
|
||||||
repository,
|
repository,
|
||||||
branch,
|
branch,
|
||||||
|
@@ -635,7 +635,6 @@ export async function buildImage({
|
|||||||
const cache = `${applicationId}:${tag}${isCache ? '-cache' : ''}`
|
const cache = `${applicationId}:${tag}${isCache ? '-cache' : ''}`
|
||||||
const { dockerRegistry: { url, username, password } } = await prisma.application.findUnique({ where: { id: applicationId }, select: { dockerRegistry: true } })
|
const { dockerRegistry: { url, username, password } } = await prisma.application.findUnique({ where: { id: applicationId }, select: { dockerRegistry: true } })
|
||||||
const location = await saveDockerRegistryCredentials({ url, username, password, workdir })
|
const location = await saveDockerRegistryCredentials({ url, username, password, workdir })
|
||||||
console.log(`docker ${location ? `--config ${location}` : ''} build --progress plain -f ${workdir}/${dockerFile} -t ${cache} --build-arg SOURCE_COMMIT=${commit} ${workdir}`)
|
|
||||||
|
|
||||||
await executeDockerCmd({ debug, buildId, applicationId, dockerId, command: `docker ${location ? `--config ${location}` : ''} build --progress plain -f ${workdir}/${dockerFile} -t ${cache} --build-arg SOURCE_COMMIT=${commit} ${workdir}` })
|
await executeDockerCmd({ debug, buildId, applicationId, dockerId, command: `docker ${location ? `--config ${location}` : ''} build --progress plain -f ${workdir}/${dockerFile} -t ${cache} --build-arg SOURCE_COMMIT=${commit} ${workdir}` })
|
||||||
|
|
||||||
|
@@ -1587,22 +1587,44 @@ export async function cleanupDockerStorage(dockerId, lowDiskSpace, force) {
|
|||||||
}
|
}
|
||||||
} catch (error) { }
|
} catch (error) { }
|
||||||
if (lowDiskSpace || force) {
|
if (lowDiskSpace || force) {
|
||||||
// if (isDev) {
|
// Cleanup images that are not used
|
||||||
// if (!force) console.log(`[DEV MODE] Low disk space: ${lowDiskSpace}`);
|
try {
|
||||||
// return;
|
await executeDockerCmd({ dockerId, command: `docker image prune -f` });
|
||||||
// }
|
} catch (error) { }
|
||||||
|
|
||||||
|
const { numberOfDockerImagesKeptLocally } = await prisma.setting.findUnique({ where: { id: '0' } })
|
||||||
|
const { stdout: images } = await executeDockerCmd({
|
||||||
|
dockerId,
|
||||||
|
command: `docker images | grep -v "<none>" | grep -v REPOSITORY | awk '{print $1, $2}'`
|
||||||
|
});
|
||||||
|
const imagesArray = images.trim().replaceAll(' ', ':').split('\n');
|
||||||
|
const imagesSet = new Set(imagesArray.map((image) => image.split(':')[0]));
|
||||||
|
let deleteImage = []
|
||||||
|
for (const image of imagesSet) {
|
||||||
|
let keepImage = []
|
||||||
|
for (const image2 of imagesArray) {
|
||||||
|
if (image2.startsWith(image)) {
|
||||||
|
if (keepImage.length >= numberOfDockerImagesKeptLocally) {
|
||||||
|
deleteImage.push(image2)
|
||||||
|
} else {
|
||||||
|
keepImage.push(image2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const image of deleteImage) {
|
||||||
|
await executeDockerCmd({ dockerId, command: `docker image rm -f ${image}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prune coolify managed containers
|
||||||
try {
|
try {
|
||||||
await executeDockerCmd({
|
await executeDockerCmd({
|
||||||
dockerId,
|
dockerId,
|
||||||
command: `docker container prune -f --filter "label=coolify.managed=true"`
|
command: `docker container prune -f --filter "label=coolify.managed=true"`
|
||||||
});
|
});
|
||||||
} catch (error) { }
|
} catch (error) { }
|
||||||
try {
|
|
||||||
await executeDockerCmd({ dockerId, command: `docker image prune -f` });
|
|
||||||
} catch (error) { }
|
|
||||||
try {
|
|
||||||
await executeDockerCmd({ dockerId, command: `docker image prune -a -f` });
|
|
||||||
} catch (error) { }
|
|
||||||
// Cleanup build caches
|
// Cleanup build caches
|
||||||
try {
|
try {
|
||||||
await executeDockerCmd({ dockerId, command: `docker builder prune -a -f` });
|
await executeDockerCmd({ dockerId, command: `docker builder prune -a -f` });
|
||||||
|
@@ -53,7 +53,8 @@ export async function listAllSettings(request: FastifyRequest) {
|
|||||||
}
|
}
|
||||||
export async function saveSettings(request: FastifyRequest<SaveSettings>, reply: FastifyReply) {
|
export async function saveSettings(request: FastifyRequest<SaveSettings>, reply: FastifyReply) {
|
||||||
try {
|
try {
|
||||||
const {
|
let {
|
||||||
|
numberOfDockerImagesKeptLocally,
|
||||||
doNotTrack,
|
doNotTrack,
|
||||||
fqdn,
|
fqdn,
|
||||||
isAPIDebuggingEnabled,
|
isAPIDebuggingEnabled,
|
||||||
@@ -67,9 +68,12 @@ export async function saveSettings(request: FastifyRequest<SaveSettings>, reply:
|
|||||||
proxyDefaultRedirect
|
proxyDefaultRedirect
|
||||||
} = request.body
|
} = request.body
|
||||||
const { id } = await listSettings();
|
const { id } = await listSettings();
|
||||||
|
if (numberOfDockerImagesKeptLocally) {
|
||||||
|
numberOfDockerImagesKeptLocally = Number(numberOfDockerImagesKeptLocally)
|
||||||
|
}
|
||||||
await prisma.setting.update({
|
await prisma.setting.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { doNotTrack, isRegistrationEnabled, dualCerts, isAutoUpdateEnabled, isDNSCheckEnabled, DNSServers, isAPIDebuggingEnabled, }
|
data: { numberOfDockerImagesKeptLocally, doNotTrack, isRegistrationEnabled, dualCerts, isAutoUpdateEnabled, isDNSCheckEnabled, DNSServers, isAPIDebuggingEnabled, }
|
||||||
});
|
});
|
||||||
if (fqdn) {
|
if (fqdn) {
|
||||||
await prisma.setting.update({ where: { id }, data: { fqdn } });
|
await prisma.setting.update({ where: { id }, data: { fqdn } });
|
||||||
|
@@ -2,6 +2,7 @@ import { OnlyId } from "../../../../types"
|
|||||||
|
|
||||||
export interface SaveSettings {
|
export interface SaveSettings {
|
||||||
Body: {
|
Body: {
|
||||||
|
numberOfDockerImagesKeptLocally: number,
|
||||||
doNotTrack: boolean,
|
doNotTrack: boolean,
|
||||||
fqdn: string,
|
fqdn: string,
|
||||||
isAPIDebuggingEnabled: boolean,
|
isAPIDebuggingEnabled: boolean,
|
||||||
|
@@ -517,20 +517,28 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="repository">Git commit</label>
|
<label for="repository">Git commit</label>
|
||||||
{#if isDisabled}
|
<div class="flex gap-2">
|
||||||
<input
|
<input
|
||||||
class="w-full"
|
class="w-full"
|
||||||
disabled={isDisabled || application.settings.isPublicRepository}
|
disabled={isDisabled}
|
||||||
placeholder="default: latest commit"
|
placeholder="default: latest commit"
|
||||||
bind:value={application.gitCommitHash}
|
bind:value={application.gitCommitHash}
|
||||||
/>
|
/>
|
||||||
{:else}
|
<a href={application.gitSource.htmlUrl}/{application.repository}/commits/{application.branch} target="_blank" rel="noreferrer" class="btn btn-primary text-xs" >Commits<svg
|
||||||
<input
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
class="w-full"
|
fill="currentColor"
|
||||||
placeholder="default: latest commit"
|
viewBox="0 0 24 24"
|
||||||
bind:value={application.gitCommitHash}
|
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"
|
||||||
/>
|
/>
|
||||||
{/if}
|
</svg></a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<label for="repository">{$t('application.git_repository')}</label>
|
<label for="repository">{$t('application.git_repository')}</label>
|
||||||
|
@@ -36,6 +36,7 @@
|
|||||||
let maxPort = settings.maxPort;
|
let maxPort = settings.maxPort;
|
||||||
let proxyDefaultRedirect = settings.proxyDefaultRedirect;
|
let proxyDefaultRedirect = settings.proxyDefaultRedirect;
|
||||||
let doNotTrack = settings.doNotTrack;
|
let doNotTrack = settings.doNotTrack;
|
||||||
|
let numberOfDockerImagesKeptLocally = settings.numberOfDockerImagesKeptLocally;
|
||||||
|
|
||||||
let forceSave = false;
|
let forceSave = false;
|
||||||
let fqdn = settings.fqdn;
|
let fqdn = settings.fqdn;
|
||||||
@@ -165,6 +166,9 @@
|
|||||||
if (proxyDefaultRedirect !== settings.proxyDefaultRedirect) {
|
if (proxyDefaultRedirect !== settings.proxyDefaultRedirect) {
|
||||||
await post(`/settings`, { proxyDefaultRedirect });
|
await post(`/settings`, { proxyDefaultRedirect });
|
||||||
}
|
}
|
||||||
|
if (numberOfDockerImagesKeptLocally !== settings.numberOfDockerImagesKeptLocally) {
|
||||||
|
await post(`/settings`, { numberOfDockerImagesKeptLocally });
|
||||||
|
}
|
||||||
if (minPort !== settings.minPort || maxPort !== settings.maxPort) {
|
if (minPort !== settings.minPort || maxPort !== settings.maxPort) {
|
||||||
await post(`/settings`, { minPort, maxPort });
|
await post(`/settings`, { minPort, maxPort });
|
||||||
settings.minPort = minPort;
|
settings.minPort = minPort;
|
||||||
@@ -393,6 +397,25 @@
|
|||||||
on:click|preventDefault|stopPropagation={rollback}>Rollback</button
|
on:click|preventDefault|stopPropagation={rollback}>Rollback</button
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="grid grid-cols-2 items-center">
|
||||||
|
<div>
|
||||||
|
Number of Docker Images kept locally
|
||||||
|
<Explainer
|
||||||
|
position="dropdown-bottom"
|
||||||
|
explanation="The number of Docker images kept locally on the server for EACH application. The oldest images will be deleted when the limit is reached.<br><br>Useful to rollback to a specific version of your applications quickly, but it will use more storage locally."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
class="w-full"
|
||||||
|
bind:value={numberOfDockerImagesKeptLocally}
|
||||||
|
readonly={!$appSession.isAdmin}
|
||||||
|
disabled={!$appSession.isAdmin}
|
||||||
|
name="numberOfDockerImagesKeptLocally"
|
||||||
|
id="numberOfDockerImagesKeptLocally"
|
||||||
|
placeholder="default: 3"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="grid grid-cols-2 items-center">
|
<div class="grid grid-cols-2 items-center">
|
||||||
<div>
|
<div>
|
||||||
{$t('forms.public_port_range')}
|
{$t('forms.public_port_range')}
|
||||||
|
Reference in New Issue
Block a user