forked from syntaxbullet/aurorabot
feat: add web asset rebuilding to update command and consolidate post-restart messages
- Detect web/src/** changes and trigger frontend rebuild after updates - Add buildWebAssets flag to RestartContext and needsWebBuild to UpdateCheckResult - Consolidate post-restart progress into single editable message - Delete progress message after completion, show only final result
This commit is contained in:
@@ -97,6 +97,7 @@ async function handleUpdate(interaction: any) {
|
|||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
runMigrations: requirements.needsMigrations,
|
runMigrations: requirements.needsMigrations,
|
||||||
installDependencies: requirements.needsRootInstall || requirements.needsWebInstall,
|
installDependencies: requirements.needsRootInstall || requirements.needsWebInstall,
|
||||||
|
buildWebAssets: requirements.needsWebBuild,
|
||||||
previousCommit: previousCommit.substring(0, 7),
|
previousCommit: previousCommit.substring(0, 7),
|
||||||
newCommit: updateInfo.latestCommit
|
newCommit: updateInfo.latestCommit
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export interface RestartContext {
|
|||||||
timestamp: number;
|
timestamp: number;
|
||||||
runMigrations: boolean;
|
runMigrations: boolean;
|
||||||
installDependencies: boolean;
|
installDependencies: boolean;
|
||||||
|
buildWebAssets: boolean;
|
||||||
previousCommit: string;
|
previousCommit: string;
|
||||||
newCommit: string;
|
newCommit: string;
|
||||||
}
|
}
|
||||||
@@ -12,6 +13,7 @@ export interface RestartContext {
|
|||||||
export interface UpdateCheckResult {
|
export interface UpdateCheckResult {
|
||||||
needsRootInstall: boolean;
|
needsRootInstall: boolean;
|
||||||
needsWebInstall: boolean;
|
needsWebInstall: boolean;
|
||||||
|
needsWebBuild: boolean;
|
||||||
needsMigrations: boolean;
|
needsMigrations: boolean;
|
||||||
changedFiles: string[];
|
changedFiles: string[];
|
||||||
error?: Error;
|
error?: Error;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function getUpdatesAvailableMessage(
|
|||||||
force: boolean
|
force: boolean
|
||||||
) {
|
) {
|
||||||
const { branch, currentCommit, latestCommit, commitCount, commits } = updateInfo;
|
const { branch, currentCommit, latestCommit, commitCount, commits } = updateInfo;
|
||||||
const { needsRootInstall, needsWebInstall, needsMigrations } = requirements;
|
const { needsRootInstall, needsWebInstall, needsWebBuild, needsMigrations } = requirements;
|
||||||
|
|
||||||
// Build commit list (max 5)
|
// Build commit list (max 5)
|
||||||
const commitList = commits
|
const commitList = commits
|
||||||
@@ -50,6 +50,7 @@ export function getUpdatesAvailableMessage(
|
|||||||
const reqs: string[] = [];
|
const reqs: string[] = [];
|
||||||
if (needsRootInstall) reqs.push("📦 Install root dependencies");
|
if (needsRootInstall) reqs.push("📦 Install root dependencies");
|
||||||
if (needsWebInstall) reqs.push("🌐 Install web dependencies");
|
if (needsWebInstall) reqs.push("🌐 Install web dependencies");
|
||||||
|
if (needsWebBuild) reqs.push("🏗️ Build web dashboard");
|
||||||
if (needsMigrations) reqs.push("🗃️ Run database migrations");
|
if (needsMigrations) reqs.push("🗃️ Run database migrations");
|
||||||
if (reqs.length === 0) reqs.push("⚡ Quick update (no extra steps)");
|
if (reqs.length === 0) reqs.push("⚡ Quick update (no extra steps)");
|
||||||
|
|
||||||
@@ -124,6 +125,9 @@ export function getUpdatingEmbed(requirements: UpdateCheckResult) {
|
|||||||
if (requirements.needsRootInstall || requirements.needsWebInstall) {
|
if (requirements.needsRootInstall || requirements.needsWebInstall) {
|
||||||
steps.push("📦 Dependencies will be installed after restart");
|
steps.push("📦 Dependencies will be installed after restart");
|
||||||
}
|
}
|
||||||
|
if (requirements.needsWebBuild) {
|
||||||
|
steps.push("🏗️ Web dashboard will be rebuilt after restart");
|
||||||
|
}
|
||||||
if (requirements.needsMigrations) {
|
if (requirements.needsMigrations) {
|
||||||
steps.push("🗃️ Migrations will run after restart");
|
steps.push("🗃️ Migrations will run after restart");
|
||||||
}
|
}
|
||||||
@@ -157,16 +161,19 @@ export function getErrorEmbed(error: unknown) {
|
|||||||
export interface PostRestartResult {
|
export interface PostRestartResult {
|
||||||
installSuccess: boolean;
|
installSuccess: boolean;
|
||||||
installOutput: string;
|
installOutput: string;
|
||||||
|
webBuildSuccess: boolean;
|
||||||
|
webBuildOutput: string;
|
||||||
migrationSuccess: boolean;
|
migrationSuccess: boolean;
|
||||||
migrationOutput: string;
|
migrationOutput: string;
|
||||||
ranInstall: boolean;
|
ranInstall: boolean;
|
||||||
|
ranWebBuild: boolean;
|
||||||
ranMigrations: boolean;
|
ranMigrations: boolean;
|
||||||
previousCommit?: string;
|
previousCommit?: string;
|
||||||
newCommit?: string;
|
newCommit?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPostRestartEmbed(result: PostRestartResult, hasRollback: boolean) {
|
export function getPostRestartEmbed(result: PostRestartResult, hasRollback: boolean) {
|
||||||
const isSuccess = result.installSuccess && result.migrationSuccess;
|
const isSuccess = result.installSuccess && result.webBuildSuccess && result.migrationSuccess;
|
||||||
|
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle(isSuccess ? "✅ Update Complete" : "⚠️ Update Completed with Issues")
|
.setTitle(isSuccess ? "✅ Update Complete" : "⚠️ Update Completed with Issues")
|
||||||
@@ -192,6 +199,13 @@ export function getPostRestartEmbed(result: PostRestartResult, hasRollback: bool
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result.ranWebBuild) {
|
||||||
|
results.push(result.webBuildSuccess
|
||||||
|
? "✅ Web dashboard built"
|
||||||
|
: "❌ Web dashboard build failed"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (result.ranMigrations) {
|
if (result.ranMigrations) {
|
||||||
results.push(result.migrationSuccess
|
results.push(result.migrationSuccess
|
||||||
? "✅ Migrations applied"
|
? "✅ Migrations applied"
|
||||||
@@ -216,6 +230,14 @@ export function getPostRestartEmbed(result: PostRestartResult, hasRollback: bool
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result.webBuildOutput && !result.webBuildSuccess) {
|
||||||
|
embed.addFields({
|
||||||
|
name: "Web Build Output",
|
||||||
|
value: `\`\`\`\n${truncate(result.webBuildOutput, OUTPUT_TRUNCATE_LENGTH)}\n\`\`\``,
|
||||||
|
inline: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (result.migrationOutput && !result.migrationSuccess) {
|
if (result.migrationOutput && !result.migrationSuccess) {
|
||||||
embed.addFields({
|
embed.addFields({
|
||||||
name: "Migration Output",
|
name: "Migration Output",
|
||||||
@@ -259,6 +281,66 @@ export function getRunningMigrationsEmbed() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getBuildingWebEmbed() {
|
||||||
|
return createInfoEmbed(
|
||||||
|
"🌐 Building web dashboard assets...\nThis may take a moment.",
|
||||||
|
"⏳ Building Web Dashboard"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PostRestartProgress {
|
||||||
|
installDeps: boolean;
|
||||||
|
buildWeb: boolean;
|
||||||
|
runMigrations: boolean;
|
||||||
|
currentStep: "starting" | "install" | "build" | "migrate" | "done";
|
||||||
|
installDone?: boolean;
|
||||||
|
buildDone?: boolean;
|
||||||
|
migrateDone?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPostRestartProgressEmbed(progress: PostRestartProgress) {
|
||||||
|
const steps: string[] = [];
|
||||||
|
|
||||||
|
// Installation step
|
||||||
|
if (progress.installDeps) {
|
||||||
|
if (progress.currentStep === "install") {
|
||||||
|
steps.push("⏳ Installing dependencies...");
|
||||||
|
} else if (progress.installDone) {
|
||||||
|
steps.push("✅ Dependencies installed");
|
||||||
|
} else {
|
||||||
|
steps.push("⬚ Install dependencies");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Web build step
|
||||||
|
if (progress.buildWeb) {
|
||||||
|
if (progress.currentStep === "build") {
|
||||||
|
steps.push("⏳ Building web dashboard...");
|
||||||
|
} else if (progress.buildDone) {
|
||||||
|
steps.push("✅ Web dashboard built");
|
||||||
|
} else {
|
||||||
|
steps.push("⬚ Build web dashboard");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrations step
|
||||||
|
if (progress.runMigrations) {
|
||||||
|
if (progress.currentStep === "migrate") {
|
||||||
|
steps.push("⏳ Running migrations...");
|
||||||
|
} else if (progress.migrateDone) {
|
||||||
|
steps.push("✅ Migrations applied");
|
||||||
|
} else {
|
||||||
|
steps.push("⬚ Run migrations");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (steps.length === 0) {
|
||||||
|
steps.push("⚡ Quick restart (no extra steps needed)");
|
||||||
|
}
|
||||||
|
|
||||||
|
return createInfoEmbed(steps.join("\n"), "🔄 Post-Update Tasks");
|
||||||
|
}
|
||||||
|
|
||||||
export function getRollbackSuccessEmbed(commit: string) {
|
export function getRollbackSuccessEmbed(commit: string) {
|
||||||
return createSuccessEmbed(
|
return createSuccessEmbed(
|
||||||
`Successfully rolled back to commit \`${commit}\`.\nThe bot will restart now.`,
|
`Successfully rolled back to commit \`${commit}\`.\nThe bot will restart now.`,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { exec } from "child_process";
|
|||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
import { writeFile, readFile, unlink } from "fs/promises";
|
import { writeFile, readFile, unlink } from "fs/promises";
|
||||||
import { Client, TextChannel } from "discord.js";
|
import { Client, TextChannel } from "discord.js";
|
||||||
import { getPostRestartEmbed, getInstallingDependenciesEmbed, getRunningMigrationsEmbed } from "@/modules/admin/update.view";
|
import { getPostRestartEmbed, getPostRestartProgressEmbed, type PostRestartProgress } from "@/modules/admin/update.view";
|
||||||
import type { PostRestartResult } from "@/modules/admin/update.view";
|
import type { PostRestartResult } from "@/modules/admin/update.view";
|
||||||
import type { RestartContext, UpdateCheckResult, UpdateInfo, CommitInfo } from "@/modules/admin/update.types";
|
import type { RestartContext, UpdateCheckResult, UpdateInfo, CommitInfo } from "@/modules/admin/update.types";
|
||||||
|
|
||||||
@@ -70,6 +70,14 @@ export class UpdateService {
|
|||||||
file === "web/package.json" || file === "web/bun.lock"
|
file === "web/package.json" || file === "web/bun.lock"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Detect if web source files changed (requires rebuild)
|
||||||
|
const needsWebBuild = changedFiles.some(file =>
|
||||||
|
file.startsWith("web/src/") ||
|
||||||
|
file === "web/build.ts" ||
|
||||||
|
file === "web/tailwind.config.ts" ||
|
||||||
|
file === "web/tsconfig.json"
|
||||||
|
);
|
||||||
|
|
||||||
const needsMigrations = changedFiles.some(file =>
|
const needsMigrations = changedFiles.some(file =>
|
||||||
file.includes("schema.ts") || file.startsWith("drizzle/")
|
file.includes("schema.ts") || file.startsWith("drizzle/")
|
||||||
);
|
);
|
||||||
@@ -77,6 +85,7 @@ export class UpdateService {
|
|||||||
return {
|
return {
|
||||||
needsRootInstall,
|
needsRootInstall,
|
||||||
needsWebInstall,
|
needsWebInstall,
|
||||||
|
needsWebBuild,
|
||||||
needsMigrations,
|
needsMigrations,
|
||||||
changedFiles
|
changedFiles
|
||||||
};
|
};
|
||||||
@@ -85,6 +94,7 @@ export class UpdateService {
|
|||||||
return {
|
return {
|
||||||
needsRootInstall: false,
|
needsRootInstall: false,
|
||||||
needsWebInstall: false,
|
needsWebInstall: false,
|
||||||
|
needsWebBuild: false,
|
||||||
needsMigrations: false,
|
needsMigrations: false,
|
||||||
changedFiles: [],
|
changedFiles: [],
|
||||||
error: e instanceof Error ? e : new Error(String(e))
|
error: e instanceof Error ? e : new Error(String(e))
|
||||||
@@ -259,43 +269,100 @@ export class UpdateService {
|
|||||||
const result: PostRestartResult = {
|
const result: PostRestartResult = {
|
||||||
installSuccess: true,
|
installSuccess: true,
|
||||||
installOutput: "",
|
installOutput: "",
|
||||||
|
webBuildSuccess: true,
|
||||||
|
webBuildOutput: "",
|
||||||
migrationSuccess: true,
|
migrationSuccess: true,
|
||||||
migrationOutput: "",
|
migrationOutput: "",
|
||||||
ranInstall: context.installDependencies,
|
ranInstall: context.installDependencies,
|
||||||
|
ranWebBuild: context.buildWebAssets,
|
||||||
ranMigrations: context.runMigrations,
|
ranMigrations: context.runMigrations,
|
||||||
previousCommit: context.previousCommit,
|
previousCommit: context.previousCommit,
|
||||||
newCommit: context.newCommit
|
newCommit: context.newCommit
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Track progress for consolidated message
|
||||||
|
const progress: PostRestartProgress = {
|
||||||
|
installDeps: context.installDependencies,
|
||||||
|
buildWeb: context.buildWebAssets,
|
||||||
|
runMigrations: context.runMigrations,
|
||||||
|
currentStep: "starting"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only send progress message if there are tasks to run
|
||||||
|
const hasTasks = context.installDependencies || context.buildWebAssets || context.runMigrations;
|
||||||
|
let progressMessage = hasTasks
|
||||||
|
? await channel.send({ embeds: [getPostRestartProgressEmbed(progress)] })
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// Helper to update progress message
|
||||||
|
const updateProgress = async () => {
|
||||||
|
if (progressMessage) {
|
||||||
|
await progressMessage.edit({ embeds: [getPostRestartProgressEmbed(progress)] });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 1. Install Dependencies if needed
|
// 1. Install Dependencies if needed
|
||||||
if (context.installDependencies) {
|
if (context.installDependencies) {
|
||||||
try {
|
try {
|
||||||
await channel.send({ embeds: [getInstallingDependenciesEmbed()] });
|
progress.currentStep = "install";
|
||||||
|
await updateProgress();
|
||||||
|
|
||||||
const { stdout: rootOutput } = await execAsync("bun install");
|
const { stdout: rootOutput } = await execAsync("bun install");
|
||||||
const { stdout: webOutput } = await execAsync("cd web && bun install");
|
const { stdout: webOutput } = await execAsync("cd web && bun install");
|
||||||
|
|
||||||
result.installOutput = `📦 Root: ${rootOutput.trim() || "Done"}\n🌐 Web: ${webOutput.trim() || "Done"}`;
|
result.installOutput = `📦 Root: ${rootOutput.trim() || "Done"}\n🌐 Web: ${webOutput.trim() || "Done"}`;
|
||||||
|
progress.installDone = true;
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
result.installSuccess = false;
|
result.installSuccess = false;
|
||||||
result.installOutput = err instanceof Error ? err.message : String(err);
|
result.installOutput = err instanceof Error ? err.message : String(err);
|
||||||
|
progress.installDone = true; // Mark as done even on failure
|
||||||
console.error("Dependency Install Failed:", err);
|
console.error("Dependency Install Failed:", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Run Migrations
|
// 2. Build Web Assets if needed
|
||||||
|
if (context.buildWebAssets) {
|
||||||
|
try {
|
||||||
|
progress.currentStep = "build";
|
||||||
|
await updateProgress();
|
||||||
|
|
||||||
|
const { stdout } = await execAsync("cd web && bun run build");
|
||||||
|
result.webBuildOutput = stdout.trim() || "Build completed successfully";
|
||||||
|
progress.buildDone = true;
|
||||||
|
} catch (err: unknown) {
|
||||||
|
result.webBuildSuccess = false;
|
||||||
|
result.webBuildOutput = err instanceof Error ? err.message : String(err);
|
||||||
|
progress.buildDone = true;
|
||||||
|
console.error("Web Build Failed:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Run Migrations
|
||||||
if (context.runMigrations) {
|
if (context.runMigrations) {
|
||||||
try {
|
try {
|
||||||
await channel.send({ embeds: [getRunningMigrationsEmbed()] });
|
progress.currentStep = "migrate";
|
||||||
|
await updateProgress();
|
||||||
|
|
||||||
const { stdout } = await execAsync("bun x drizzle-kit migrate");
|
const { stdout } = await execAsync("bun x drizzle-kit migrate");
|
||||||
result.migrationOutput = stdout;
|
result.migrationOutput = stdout;
|
||||||
|
progress.migrateDone = true;
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
result.migrationSuccess = false;
|
result.migrationSuccess = false;
|
||||||
result.migrationOutput = err instanceof Error ? err.message : String(err);
|
result.migrationOutput = err instanceof Error ? err.message : String(err);
|
||||||
|
progress.migrateDone = true;
|
||||||
console.error("Migration Failed:", err);
|
console.error("Migration Failed:", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete progress message before final result
|
||||||
|
if (progressMessage) {
|
||||||
|
try {
|
||||||
|
await progressMessage.delete();
|
||||||
|
} catch {
|
||||||
|
// Message may already be deleted, ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user