Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ web_modules/
.env
.env.*
!.env.example
curl.txt


# parcel-bundler cache (https://parceljs.org/)
.cache
Expand Down
93 changes: 71 additions & 22 deletions client/src/lib/api.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { usePipelineStore } from "../store/usePipelineStore";

export const BASE =
import.meta.env.VITE_API_BASE ?? "http://localhost:3000/api";

Expand Down Expand Up @@ -139,35 +141,82 @@ export const api = {
return { results };
},

// --- Mock deploy APIs for Dashboard ---
async startDeploy({ repo, env }: { repo: string; env: string }) {
const jobId = `job_${Math.random().toString(36).slice(2)}`;
// Stash minimal job info in memory for the stream to reference
JOBS.set(jobId, { repo, env, startedAt: Date.now() });
return { jobId } as const;
},
// --- Deploy APIs for Dashboard ---
async startDeploy({
repoFullName: fromCallerRepo,
branch,
env,
yaml: fromCallerYaml,
provider,
path,
}: {
repoFullName?: string;
branch?: string;
env?: string;
yaml?: string;
provider?: string;
path?: string;
}) {
const pipelineStore = usePipelineStore.getState();
const repoFullName = fromCallerRepo || pipelineStore?.repoFullName || pipelineStore?.result?.repo;
const selectedBranch = branch || pipelineStore?.selectedBranch || "main";
const yaml = pipelineStore?.result?.generated_yaml;
const environment = env || pipelineStore?.environment || "dev";

const providerFinal = provider || pipelineStore?.provider || "aws";
const pathFinal = path || `.github/workflows/${environment}-deploy.yml`;

console.group("[Deploy Debug]");
console.log("repoFullName:", repoFullName);
console.log("selectedBranch:", selectedBranch);
console.log("environment:", environment);
console.log("provider:", providerFinal);
console.log("path:", pathFinal);
console.log("YAML length:", yaml ? yaml.length : 0);
console.groupEnd();

const payload = {
repoFullName,
branch: selectedBranch,
env: environment,
yaml,
provider: providerFinal,
path: pathFinal,
};

console.log("[Deploy] Final payload:", payload);

const res = await fetch(`${SERVER_BASE}/mcp/v1/pipeline_commit`, {
method: "POST",
headers: { "Content-Type": "application/json" },
credentials: "include",
body: JSON.stringify(payload),
});

const data = await res.json().catch(() => ({}));

console.group("[Deploy Response]");
console.log("Status:", res.status);
console.log("Data:", data);
console.groupEnd();

if (!res.ok) throw new Error(`Pipeline commit failed: ${res.statusText}`);
return data;
},

streamJob(
jobId: string,
onEvent: (e: { ts: string; level: "info" | "warn" | "error"; msg: string }) => void
) {
const meta = JOBS.get(jobId) || { repo: "?", env: "dev" };
streamJob(_jobId: string, onEvent: (e: { ts: string; level: "info"; msg: string }) => void) {
const steps = [
`Authenticating to AWS (${meta.env})`,
`Assuming role`,
`Validating permissions`,
`Building artifacts`,
`Deploying ${meta.repo}`,
`Verifying rollout`,
`Done`
"Connecting to GitHub...",
"Committing workflow file...",
"Verifying commit...",
"Done ✅"
];
let i = 0;
const timer = setInterval(() => {
if (i >= steps.length) return;
const level = i === steps.length - 1 ? "info" : "info";
onEvent({ ts: new Date().toISOString(), level, msg: steps[i++] });
onEvent({ ts: new Date().toISOString(), level: "info", msg: steps[i++] });
if (i >= steps.length) clearInterval(timer);
}, 800);
}, 600);
return () => clearInterval(timer);
},
};
Expand Down
2 changes: 1 addition & 1 deletion client/src/pages/ConfigurePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export default function ConfigurePage() {

<div>
<div>YAML Preview</div>
<pre style={{ maxHeight: 400, overflow: "auto", background: "#f6f6f6", padding: 12 }}>
<pre style={{ maxHeight: 400, overflow: "auto", background: "#131212ff", padding: 12 }}>
{pipeline.result?.yaml ?? pipeline.result?.generated_yaml ?? "Click Generate Pipeline to preview YAML…"}
</pre>
</div>
Expand Down
38 changes: 34 additions & 4 deletions client/src/pages/DashboardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ import { useDeployStore } from "../store/useDeployStore";
export default function DashboardPage() {
const { repo } = useRepoStore();
const { result } = usePipelineStore();
console.log("Debug: result.generated_yaml =", result?.generated_yaml);
const cfg = useConfigStore();

console.log("Debug: repo =", repo);
console.log("Debug: cfg.env =", cfg.env);
console.log("Debug: result =", result);

// Select stable slices from the deploy store to avoid effect loops
const running = useDeployStore((s) => s.running);
const events = useDeployStore((s) => s.events);
Expand All @@ -34,23 +39,48 @@ export default function DashboardPage() {
<div style={{ color: "#666", fontSize: 12 }}>
{result?.pipeline_name} · Generated {result ? new Date(result.created_at).toLocaleString() : "—"}
</div>
<pre style={{ maxHeight: 280, overflow: "auto", background: "#f6f6f6", padding: 12 }}>
<pre style={{ maxHeight: 280, overflow: "auto", background: "#1e1c1cff", padding: 12 }}>
{result?.generated_yaml ?? "No pipeline generated yet."}
</pre>
</div>

<div>
<button
disabled={running}
onClick={() => startDeploy({ repo: repo!, env: cfg.env })}
onClick={() => {
const repoFullName = result?.repo || repo;
const yaml = result?.generated_yaml;
const branch = result?.branch || "main";
const environment = cfg.env || "dev";
const provider = "aws";
const path = `.github/workflows/${environment}-deploy.yml`;

console.log("Deploy button clicked with payload:", {
repoFullName,
branch,
env: environment,
yaml: yaml ? yaml.slice(0, 100) + "..." : "No YAML",
provider,
path,
});

startDeploy({
repoFullName,
branch,
env: environment,
yaml,
provider,
path,
});
}}
>
{running ? "Deploying…" : "Deploy to AWS"}
{running ? "Committing…" : "Commit to GitHub"}
</button>
{running && <button onClick={stop} style={{ marginLeft: 8 }}>Stop</button>}
</div>

<div>
<strong>Logs</strong>
<strong>Commit Logs</strong>
<div style={{ height: 280, overflow: "auto", background: "#111", color: "#ddd", padding: 12, fontFamily: "monospace", fontSize: 12 }}>
{events.length === 0 ? "No logs yet." :
events.map((e, i) => (
Expand Down
18 changes: 16 additions & 2 deletions client/src/pages/DeployPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,29 @@ export default function DeployPage() {
// loadRoles();
}, []);

function startCommit() {
// Placeholder function to start commit process
console.log("Commit to GitHub started");
}

return (
<div className="p-6 max-w-2xl mx-auto space-y-4">
<h2 className="text-lg font-semibold">Commit Workflow to GitHub</h2>
{!repo || !branch ? (
<p className="text-sm text-orange-700">Pick a repo/branch on Connect first.</p>
) : (
<p className="text-sm opacity-80">Deploying <strong>{repo}</strong> @ <strong>{branch}</strong></p>
<>
<p className="text-sm opacity-80">Committing workflow to <strong>GitHub</strong> for <strong>{repo}</strong> @ <strong>{branch}</strong></p>
<button
className="mt-2 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
onClick={startCommit}
>
Commit to GitHub
</button>
</>
)}

{/* role picker + open PR button go here, wired to your deploy store */}
{/* role picker + commit to GitHub button go here, wired to your deploy store */}
</div>
);
}
28 changes: 22 additions & 6 deletions client/src/store/useDeployStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,29 @@ type DeployActions = {
export const useDeployStore = create<DeployState & DeployActions>()((set) => ({
running: false,
events: [],
async startDeploy({ repo, env }) {
async startDeploy(payload: {
repoFullName: string;
branch?: string;
env: string;
yaml?: string;
provider?: string;
path?: string;
}) {
const { repoFullName, branch = "main", env, yaml, provider = "aws", path = `.github/workflows/${env}-deploy.yml` } = payload || {};
set({ running: true, events: [] });
const { jobId } = await api.startDeploy({ repo, env });
const stop = api.streamJob(jobId, (e) =>
set((s) => ({ events: [...s.events, e] }))
);
set({ jobId, stopStream: stop });
try {
console.group("[useDeployStore.startDeploy] Prepared payload");
console.log({ repoFullName, branch, env, provider, path, yamlLength: yaml ? yaml.length : 0 });
console.groupEnd();
const { jobId } = await api.startDeploy({ repoFullName, branch, env, yaml, provider, path });
const stop = api.streamJob(jobId, (e) =>
set((s) => ({ events: [...s.events, e] }))
);
set({ jobId, stopStream: stop });
} catch (err) {
console.error("[useDeployStore.startDeploy] Error:", err);
set({ running: false });
}
},
stop() {
set((s) => {
Expand Down
11 changes: 10 additions & 1 deletion client/src/store/usePipelineStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type PipelineState = {

// outputs from MCP
result?: McpPipeline;
repoFullName?: string;

// local UI state
roles: { name: string; arn: string }[];
Expand Down Expand Up @@ -117,8 +118,16 @@ export const usePipelineStore = create<PipelineState & PipelineActions>()((set,
res?.generated_yaml ||
"";

const repoFullName =
res?.data?.data?.repo ||
res?.data?.repo ||
"";

console.log("[usePipelineStore] Captured repoFullName:", repoFullName);

set({
result: { ...res, yaml: generated_yaml },
result: { ...res, yaml: generated_yaml, generated_yaml },
repoFullName,
status: "success",
editing: false,
editedYaml: undefined,
Expand Down