Deploy button disabled when container runs latest code
- Track deployed git hash per project in deployed-hashes.json - Compare main branch hash with last deployed hash - Deploy button disabled when no new code to deploy - Save hash after successful deploy - Also: CentralStore dev synced with main Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
node_modules/
|
||||
dist/
|
||||
*.js.map
|
||||
deployed-hashes.json
|
||||
|
||||
+5
-2
@@ -11,6 +11,7 @@ export interface ProjectStatusDTO {
|
||||
devAheadOfMain: number;
|
||||
lastCommit: string;
|
||||
lastCommitDev: string;
|
||||
deployNeeded: boolean;
|
||||
services: ServiceStatusDTO[];
|
||||
}
|
||||
|
||||
@@ -32,15 +33,17 @@ for (const [key, cfg] of Object.entries(PROJECTS)) {
|
||||
export function getStatusAll(): ProjectStatusDTO[] {
|
||||
const containers = getRunningContainers();
|
||||
return getAllProjects().map((p) => {
|
||||
const git = getGitStatus(p);
|
||||
const key = PROJECT_KEYS[p.name] ?? p.name.toLowerCase();
|
||||
const git = getGitStatus(p, key);
|
||||
return {
|
||||
name: p.name,
|
||||
key: PROJECT_KEYS[p.name] ?? p.name.toLowerCase(),
|
||||
key,
|
||||
branch: git.currentBranch,
|
||||
behindRemote: git.behindRemote,
|
||||
devAheadOfMain: git.devAheadOfMain,
|
||||
lastCommit: git.lastCommit,
|
||||
lastCommitDev: git.lastCommitDev,
|
||||
deployNeeded: git.deployNeeded,
|
||||
services: p.services.map((s) => {
|
||||
const c = containers.find((ct) => ct.name === s.container);
|
||||
return {
|
||||
|
||||
+7
-2
@@ -2,6 +2,7 @@ import http from "node:http";
|
||||
import { spawn } from "node:child_process";
|
||||
import { PROJECTS, type ProjectConfig } from "./config.js";
|
||||
import { exec } from "./shell.js";
|
||||
import { saveDeployedHash } from "./git.js";
|
||||
|
||||
type StepStatus = "pending" | "running" | "done" | "error";
|
||||
|
||||
@@ -65,7 +66,7 @@ function getMergeSteps(project: ProjectConfig): Step[] {
|
||||
];
|
||||
}
|
||||
|
||||
async function runSSESteps(res: http.ServerResponse, project: ProjectConfig, steps: Step[]) {
|
||||
async function runSSESteps(res: http.ServerResponse, project: ProjectConfig, steps: Step[], onSuccess?: () => void) {
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "text/event-stream",
|
||||
"Cache-Control": "no-cache",
|
||||
@@ -95,6 +96,7 @@ async function runSSESteps(res: http.ServerResponse, project: ProjectConfig, ste
|
||||
}
|
||||
|
||||
if (!failed) {
|
||||
onSuccess?.();
|
||||
sendSSE(res, "done", { success: true });
|
||||
}
|
||||
|
||||
@@ -118,5 +120,8 @@ export async function handleDeploy(req: http.IncomingMessage, res: http.ServerRe
|
||||
res.end("Unknown project");
|
||||
return;
|
||||
}
|
||||
await runSSESteps(res, project, getDeploySteps(project));
|
||||
await runSSESteps(res, project, getDeploySteps(project), () => {
|
||||
const hash = exec("git rev-parse main", project.path).stdout;
|
||||
if (hash) saveDeployedHash(projectKey, hash);
|
||||
});
|
||||
}
|
||||
|
||||
+36
-1
@@ -1,5 +1,30 @@
|
||||
import { exec, execLive } from "./shell.js";
|
||||
import type { ProjectConfig } from "./config.js";
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||
import { join, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const DEPLOYED_FILE = join(__dirname, "..", "deployed-hashes.json");
|
||||
|
||||
function loadDeployedHashes(): Record<string, string> {
|
||||
if (!existsSync(DEPLOYED_FILE)) return {};
|
||||
try {
|
||||
return JSON.parse(readFileSync(DEPLOYED_FILE, "utf-8"));
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
export function saveDeployedHash(projectKey: string, hash: string) {
|
||||
const data = loadDeployedHashes();
|
||||
data[projectKey] = hash;
|
||||
writeFileSync(DEPLOYED_FILE, JSON.stringify(data, null, 2));
|
||||
}
|
||||
|
||||
export function getDeployedHash(projectKey: string): string | null {
|
||||
return loadDeployedHashes()[projectKey] ?? null;
|
||||
}
|
||||
|
||||
export interface GitStatus {
|
||||
currentBranch: string;
|
||||
@@ -7,9 +32,12 @@ export interface GitStatus {
|
||||
devAheadOfMain: number;
|
||||
lastCommit: string;
|
||||
lastCommitDev: string;
|
||||
mainHash: string;
|
||||
deployedHash: string | null;
|
||||
deployNeeded: boolean;
|
||||
}
|
||||
|
||||
export function getGitStatus(project: ProjectConfig): GitStatus {
|
||||
export function getGitStatus(project: ProjectConfig, projectKey?: string): GitStatus {
|
||||
const branch = exec("git branch --show-current", project.path).stdout || "unknown";
|
||||
exec("git fetch --all --quiet", project.path);
|
||||
const behind = exec(`git rev-list HEAD..origin/${branch} --count`, project.path);
|
||||
@@ -21,12 +49,19 @@ export function getGitStatus(project: ProjectConfig): GitStatus {
|
||||
|
||||
const lastCommitDev = exec("git log dev -1 --oneline 2>/dev/null", project.path).stdout || "";
|
||||
|
||||
const mainHash = exec("git rev-parse main 2>/dev/null", project.path).stdout || "";
|
||||
const deployedHash = projectKey ? getDeployedHash(projectKey) : null;
|
||||
const deployNeeded = devAheadCount > 0 || (!!mainHash && mainHash !== deployedHash);
|
||||
|
||||
return {
|
||||
currentBranch: branch,
|
||||
behindRemote: behindCount,
|
||||
devAheadOfMain: devAheadCount,
|
||||
lastCommit,
|
||||
lastCommitDev,
|
||||
mainHash,
|
||||
deployedHash,
|
||||
deployNeeded,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -158,8 +158,9 @@ function render() {
|
||||
// action buttons
|
||||
const isRunning = ds?.running;
|
||||
const canDeploy = p.services.some(s => s.isApp);
|
||||
const deployDisabled = isRunning || !p.deployNeeded;
|
||||
const deployBtn = canDeploy
|
||||
? '<button class="btn btn-deploy" onclick="deploy(\\''+p.key+'\\')" '+(isRunning?'disabled':'')+'>'+
|
||||
? '<button class="btn btn-deploy" onclick="deploy(\\''+p.key+'\\')" '+(deployDisabled?'disabled':'')+'>'+
|
||||
(isRunning && ds?.action==='deploy' ? '<span class="spinning">⚙</span> Deploying...' : '▶ Deploy') + '</button>'
|
||||
: '';
|
||||
const mergeBtn = '<button class="btn btn-merge" onclick="merge(\\''+p.key+'\\')" '+(isRunning || p.devAheadOfMain===0?'disabled':'')+'>'+
|
||||
|
||||
Reference in New Issue
Block a user