feat(rollback): add rollback constraints and snapshots

- Introduced two new SQL files for rollback constraints, updating foreign key relationships with different delete actions (set null and cascade).
- Updated the journal and snapshot files to include the new rollback schema changes for versions 0096 and 0097.
- Enhanced the application service to handle rollback image tagging based on source type.
- Implemented rollback removal logic in the deployment service to ensure proper cleanup of rollback entries.
This commit is contained in:
Mauricio Siu
2025-06-21 23:17:21 -06:00
parent 8c5e34c528
commit 3d285ca437
8 changed files with 11732 additions and 17 deletions

View File

@@ -0,0 +1,3 @@
ALTER TABLE "rollback" DROP CONSTRAINT "rollback_deploymentId_deployment_deploymentId_fk";
--> statement-breakpoint
ALTER TABLE "rollback" ADD CONSTRAINT "rollback_deploymentId_deployment_deploymentId_fk" FOREIGN KEY ("deploymentId") REFERENCES "public"."deployment"("deploymentId") ON DELETE set null ON UPDATE no action;

View File

@@ -0,0 +1,3 @@
ALTER TABLE "rollback" DROP CONSTRAINT "rollback_deploymentId_deployment_deploymentId_fk";
--> statement-breakpoint
ALTER TABLE "rollback" ADD CONSTRAINT "rollback_deploymentId_deployment_deploymentId_fk" FOREIGN KEY ("deploymentId") REFERENCES "public"."deployment"("deploymentId") ON DELETE cascade ON UPDATE no action;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -673,6 +673,20 @@
"when": 1750562292392,
"tag": "0095_curly_justice",
"breakpoints": true
},
{
"idx": 96,
"version": "7",
"when": 1750566830268,
"tag": "0096_small_shaman",
"breakpoints": true
},
{
"idx": 97,
"version": "7",
"when": 1750567641441,
"tag": "0097_hard_lizard",
"breakpoints": true
}
]
}

View File

@@ -216,8 +216,12 @@ export const deployApplication = async ({
await updateApplicationStatus(applicationId, "done");
if (application.rollbackActive) {
const tagImage =
application.sourceType === "docker"
? application.dockerImage
: application.appName;
await createRollback({
appName: application.appName,
appName: tagImage || "",
deploymentId: deployment.deploymentId,
});
}

View File

@@ -31,6 +31,7 @@ import {
updatePreviewDeployment,
} from "./preview-deployment";
import { findScheduleById } from "./schedule";
import { removeRollbackById } from "./rollbacks";
export type Deployment = typeof deployments.$inferSelect;
@@ -495,6 +496,9 @@ const getDeploymentsByType = async (
const deploymentList = await db.query.deployments.findMany({
where: eq(deployments[`${type}Id`], id),
orderBy: desc(deployments.createdAt),
with: {
rollback: true,
},
});
return deploymentList;
};
@@ -529,6 +533,9 @@ const removeLastTenDeployments = async (
let command = "";
for (const oldDeployment of deploymentsToDelete) {
const logPath = path.join(oldDeployment.logPath);
if (oldDeployment.rollbackId) {
await removeRollbackById(oldDeployment.rollbackId);
}
command += `
rm -rf ${logPath};
@@ -539,8 +546,11 @@ const removeLastTenDeployments = async (
await execAsyncRemote(serverId, command);
} else {
for (const oldDeployment of deploymentsToDelete) {
if (oldDeployment.rollbackId) {
await removeRollbackById(oldDeployment.rollbackId);
}
const logPath = path.join(oldDeployment.logPath);
if (existsSync(logPath)) {
if (existsSync(logPath) && !oldDeployment.errorMessage) {
await fsPromises.unlink(logPath);
}
await removeDeployment(oldDeployment.deploymentId);

View File

@@ -1,6 +1,10 @@
import { eq } from "drizzle-orm";
import { db } from "../db";
import { type createRollbackSchema, rollbacks } from "../db/schema";
import {
type createRollbackSchema,
rollbacks,
deployments as deploymentsSchema,
} from "../db/schema";
import type { z } from "zod";
import { findApplicationById } from "./application";
import { getRemoteDocker } from "../utils/servers/remote-docker";
@@ -30,8 +34,14 @@ export const createRollback = async (
throw new Error("Deployment not found");
}
const { deployments, bitbucket, github, gitlab, gitea, ...rest } =
await findApplicationById(deployment.applicationId);
const {
deployments: _,
bitbucket,
github,
gitlab,
gitea,
...rest
} = await findApplicationById(deployment.applicationId);
await tx
.update(rollbacks)
@@ -41,6 +51,14 @@ export const createRollback = async (
})
.where(eq(rollbacks.rollbackId, rollback.rollbackId));
// Update the deployment to reference this rollback
await tx
.update(deploymentsSchema)
.set({
rollbackId: rollback.rollbackId,
})
.where(eq(deploymentsSchema.deploymentId, rollback.deploymentId));
await createRollbackImage(rest, tagImage);
return rollback;
@@ -65,12 +83,17 @@ const createRollbackImage = async (
) => {
const docker = await getRemoteDocker(application.serverId);
const result = docker.getImage(`${application.appName}:latest`);
const appTagName =
application.sourceType === "docker"
? application.dockerImage
: `${application.appName}:latest`;
const version = tagImage.split(":")[1];
const result = docker.getImage(appTagName || "");
const [repo, version] = tagImage.split(":");
await result.tag({
repo: tagImage,
repo,
tag: version,
});
};
@@ -86,28 +109,34 @@ const deleteRollbackImage = async (image: string, serverId?: string | null) => {
};
export const removeRollbackById = async (rollbackId: string) => {
const result = await db
.delete(rollbacks)
.where(eq(rollbacks.rollbackId, rollbackId))
.returning()
.then((res) => res[0]);
const rollback = await findRollbackById(rollbackId);
if (result?.image) {
if (!rollback) {
throw new Error("Rollback not found");
}
if (rollback?.image) {
try {
const deployment = await findDeploymentById(result.deploymentId);
const deployment = await findDeploymentById(rollback.deploymentId);
if (!deployment?.applicationId) {
throw new Error("Deployment not found");
}
const application = await findApplicationById(deployment.applicationId);
await deleteRollbackImage(result.image, application.serverId);
await deleteRollbackImage(rollback.image, application.serverId);
await db
.delete(rollbacks)
.where(eq(rollbacks.rollbackId, rollbackId))
.returning()
.then((res) => res[0]);
} catch (error) {
console.error(error);
}
}
return result;
return rollback;
};
export const rollback = async (rollbackId: string) => {