Skip to content

Commit e688874

Browse files
authored
Merge branch 'main' into alex
2 parents 0e78774 + 110e4b3 commit e688874

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+4963
-452
lines changed

.github/workflows/ci.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: CI
2+
on:
3+
push:
4+
branches:
5+
- main
6+
- lorenc-server
7+
8+
workflow_dispatch:
9+
inputs:
10+
environment:
11+
description: 'Target environment'
12+
required: false
13+
type: string
14+
default: 'dev'
15+
16+
jobs:
17+
build:
18+
runs-on: ubuntu-latest
19+
env:
20+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY}}
21+
PORT: 4000
22+
steps:
23+
- uses: actions/checkout@v4
24+
- uses: actions/setup-node@v4
25+
with:
26+
node-version: 20
27+
- run: npm ci
28+
- run: npm run start & sleep 3
29+
- run: npm test
30+
- run: echo "Deploying to ${{ inputs.environment || 'dev' }}..."

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ web_modules/
6969
.env
7070
.env.*
7171
!.env.example
72+
curl.txt
73+
7274

7375
# parcel-bundler cache (https://parceljs.org/)
7476
.cache
979 KB
Binary file not shown.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,4 +193,6 @@ ALTER TABLE public.deployment_logs
193193
│ Deploy Workflow Runs │
194194
│ (build, test, release) │
195195
└──────────────────────────┘
196+
197+
Test
196198
```

client/src/App.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ function NeedRepo({ children }: { children: JSX.Element }) {
1414
}
1515
function NeedPipeline({ children }: { children: JSX.Element }) {
1616
const { result } = usePipelineStore();
17-
return !result?.generated_yaml ? <Navigate to="/configure" replace /> : children;
17+
const hasYaml =
18+
result?.generated_yaml ||
19+
result?.yaml ||
20+
result?.data?.generated_yaml;
21+
22+
return !hasYaml ? <Navigate to="/configure" replace /> : children;
1823
}
1924

2025
export default function App() {

client/src/lib/api.ts

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import { usePipelineStore } from "../store/usePipelineStore";
2+
13
export const BASE =
2-
import.meta.env.VITE_API_BASE ?? "http://localhost:3333/api";
4+
import.meta.env.VITE_API_BASE ?? "http://localhost:3000/api";
35

46
// Derive the server base without any trailing "/api" for MCP calls
57
const SERVER_BASE = BASE.replace(/\/api$/, "");
@@ -37,7 +39,7 @@ export const api = {
3739
const data = await mcp<{
3840
repositories: { name: string; full_name: string; branches?: string[] }[];
3941
}>("repo_reader", {});
40-
const repos = (data?.repositories ?? []).map((r) => r.full_name);
42+
const repos = (data?.data?.repositories ?? []).map((r) => r.full_name);
4143
return { repos };
4244
},
4345

@@ -46,7 +48,7 @@ export const api = {
4648
const data = await mcp<{
4749
repositories: { name: string; full_name: string; branches?: string[] }[];
4850
}>("repo_reader", {});
49-
const item = (data?.repositories ?? []).find((r) => r.full_name === repo);
51+
const item = (data?.data?.repositories ?? []).find((r) => r.full_name === repo);
5052
return { branches: item?.branches ?? [] };
5153
},
5254

@@ -139,35 +141,82 @@ export const api = {
139141
return { results };
140142
},
141143

142-
// --- Mock deploy APIs for Dashboard ---
143-
async startDeploy({ repo, env }: { repo: string; env: string }) {
144-
const jobId = `job_${Math.random().toString(36).slice(2)}`;
145-
// Stash minimal job info in memory for the stream to reference
146-
JOBS.set(jobId, { repo, env, startedAt: Date.now() });
147-
return { jobId } as const;
148-
},
144+
// --- Deploy APIs for Dashboard ---
145+
async startDeploy({
146+
repoFullName: fromCallerRepo,
147+
branch,
148+
env,
149+
yaml: fromCallerYaml,
150+
provider,
151+
path,
152+
}: {
153+
repoFullName?: string;
154+
branch?: string;
155+
env?: string;
156+
yaml?: string;
157+
provider?: string;
158+
path?: string;
159+
}) {
160+
const pipelineStore = usePipelineStore.getState();
161+
const repoFullName = fromCallerRepo || pipelineStore?.repoFullName || pipelineStore?.result?.repo;
162+
const selectedBranch = branch || pipelineStore?.selectedBranch || "main";
163+
const yaml = pipelineStore?.result?.generated_yaml;
164+
const environment = env || pipelineStore?.environment || "dev";
165+
166+
const providerFinal = provider || pipelineStore?.provider || "aws";
167+
const pathFinal = path || `.github/workflows/${environment}-deploy.yml`;
168+
169+
console.group("[Deploy Debug]");
170+
console.log("repoFullName:", repoFullName);
171+
console.log("selectedBranch:", selectedBranch);
172+
console.log("environment:", environment);
173+
console.log("provider:", providerFinal);
174+
console.log("path:", pathFinal);
175+
console.log("YAML length:", yaml ? yaml.length : 0);
176+
console.groupEnd();
177+
178+
const payload = {
179+
repoFullName,
180+
branch: selectedBranch,
181+
env: environment,
182+
yaml,
183+
provider: providerFinal,
184+
path: pathFinal,
185+
};
186+
187+
console.log("[Deploy] Final payload:", payload);
188+
189+
const res = await fetch(`${SERVER_BASE}/mcp/v1/pipeline_commit`, {
190+
method: "POST",
191+
headers: { "Content-Type": "application/json" },
192+
credentials: "include",
193+
body: JSON.stringify(payload),
194+
});
195+
196+
const data = await res.json().catch(() => ({}));
197+
198+
console.group("[Deploy Response]");
199+
console.log("Status:", res.status);
200+
console.log("Data:", data);
201+
console.groupEnd();
202+
203+
if (!res.ok) throw new Error(`Pipeline commit failed: ${res.statusText}`);
204+
return data;
205+
},
149206

150-
streamJob(
151-
jobId: string,
152-
onEvent: (e: { ts: string; level: "info" | "warn" | "error"; msg: string }) => void
153-
) {
154-
const meta = JOBS.get(jobId) || { repo: "?", env: "dev" };
207+
streamJob(_jobId: string, onEvent: (e: { ts: string; level: "info"; msg: string }) => void) {
155208
const steps = [
156-
`Authenticating to AWS (${meta.env})`,
157-
`Assuming role`,
158-
`Validating permissions`,
159-
`Building artifacts`,
160-
`Deploying ${meta.repo}`,
161-
`Verifying rollout`,
162-
`Done`
209+
"Connecting to GitHub...",
210+
"Committing workflow file...",
211+
"Verifying commit...",
212+
"Done ✅"
163213
];
164214
let i = 0;
165215
const timer = setInterval(() => {
166216
if (i >= steps.length) return;
167-
const level = i === steps.length - 1 ? "info" : "info";
168-
onEvent({ ts: new Date().toISOString(), level, msg: steps[i++] });
217+
onEvent({ ts: new Date().toISOString(), level: "info", msg: steps[i++] });
169218
if (i >= steps.length) clearInterval(timer);
170-
}, 800);
219+
}, 600);
171220
return () => clearInterval(timer);
172221
},
173222

client/src/pages/ConfigurePage.tsx

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,39 @@
11
import { useEffect, useState } from "react";
2-
import { Link } from "react-router-dom";
2+
import { useNavigate } from "react-router-dom";
33
import { useRepoStore } from "../store/useRepoStore";
44
import { usePipelineStore } from "../store/usePipelineStore";
55

66
export default function ConfigurePage() {
77
const { repo, branch } = useRepoStore();
88
const pipeline = usePipelineStore();
9+
const navigate = useNavigate();
910

10-
// Load available AWS roles once
11+
// Log on mount with repo, branch, and navigate check
1112
useEffect(() => {
12-
pipeline.loadAwsRoles?.().catch(console.error);
13+
console.log("[ConfigurePage] Mounted. Repo:", repo, "Branch:", branch);
14+
if (!navigate) console.warn("[ConfigurePage] ⚠️ navigate() not initialized!");
15+
}, [repo, branch, navigate]);
16+
17+
// Load available AWS roles once, safely
18+
useEffect(() => {
19+
let loaded = false;
20+
21+
async function init() {
22+
if (loaded) return;
23+
loaded = true;
24+
try {
25+
console.log("[ConfigurePage] Loading AWS roles once...");
26+
await pipeline.loadAwsRoles?.();
27+
28+
// Re-read roles from store after load completes
29+
const updatedRoles = usePipelineStore.getState().roles;
30+
console.log("[ConfigurePage] Roles (after load):", updatedRoles);
31+
} catch (err) {
32+
console.error("Failed to load AWS roles:", err);
33+
}
34+
}
35+
36+
init();
1337
// eslint-disable-next-line react-hooks/exhaustive-deps
1438
}, []);
1539

@@ -22,6 +46,7 @@ export default function ConfigurePage() {
2246
setBusy(false);
2347
}
2448

49+
console.log("[ConfigurePage] pipeline.result:", pipeline.result);
2550
return (
2651
<section style={{ display: "grid", gap: 16 }}>
2752
<h1>Configure Pipeline</h1>
@@ -60,23 +85,45 @@ export default function ConfigurePage() {
6085

6186
<label>
6287
AWS Role (OIDC)
63-
<select value={pipeline.options.awsRoleArn ?? ""} onChange={(e)=>pipeline.setOption("awsRoleArn", e.target.value)} style={{ display: "block", padding: 8 }}>
88+
<select disabled={busy} value={pipeline.options.awsRoleArn ?? ""} onChange={(e)=>pipeline.setOption("awsRoleArn", e.target.value)} style={{ display: "block", padding: 8 }}>
6489
<option value="">-- select --</option>
65-
{pipeline.roles?.map((r) => <option key={r} value={r}>{r}</option>)}
90+
{pipeline.roles?.map((r) => (
91+
<option key={r.arn} value={r.arn}>
92+
{r.name}
93+
</option>
94+
))}
6695
</select>
6796
</label>
6897

6998
<div style={{ display: "flex", gap: 8 }}>
7099
<button onClick={onGenerate} disabled={busy}>{busy ? "Generating…" : "Generate Pipeline"}</button>
71-
<Link to="/secrets">
72-
<button disabled={!pipeline.result?.generated_yaml}>Continue → Secrets</button>
73-
</Link>
100+
<button
101+
onClick={() => {
102+
console.log("[ConfigurePage] Navigate button clicked.");
103+
console.log("[ConfigurePage] Pipeline result before navigating:", pipeline.result);
104+
try {
105+
navigate("/secrets", { state: { pipeline: pipeline.result } });
106+
console.log("[ConfigurePage] ✅ Navigation triggered successfully.");
107+
} catch (err) {
108+
console.error("[ConfigurePage] ❌ Navigation failed:", err);
109+
}
110+
}}
111+
disabled={
112+
!(
113+
pipeline.result?.yaml ||
114+
pipeline.result?.generated_yaml ||
115+
pipeline.result?.data?.generated_yaml
116+
)
117+
}
118+
>
119+
Continue → Secrets
120+
</button>
74121
</div>
75122

76123
<div>
77124
<div>YAML Preview</div>
78-
<pre style={{ maxHeight: 400, overflow: "auto", background: "#f6f6f6", padding: 12 }}>
79-
{pipeline.result?.generated_yaml ?? "Click Generate Pipeline to preview YAML…"}
125+
<pre style={{ maxHeight: 400, overflow: "auto", background: "#131212ff", padding: 12 }}>
126+
{pipeline.result?.yaml ?? pipeline.result?.generated_yaml ?? "Click Generate Pipeline to preview YAML…"}
80127
</pre>
81128
</div>
82129
</section>

client/src/pages/DashboardPage.tsx

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,13 @@ import { useDeployStore } from "../store/useDeployStore";
77
export default function DashboardPage() {
88
const { repo } = useRepoStore();
99
const { result } = usePipelineStore();
10+
console.log("Debug: result.generated_yaml =", result?.generated_yaml);
1011
const cfg = useConfigStore();
1112

13+
console.log("Debug: repo =", repo);
14+
console.log("Debug: cfg.env =", cfg.env);
15+
console.log("Debug: result =", result);
16+
1217
// Select stable slices from the deploy store to avoid effect loops
1318
const running = useDeployStore((s) => s.running);
1419
const events = useDeployStore((s) => s.events);
@@ -34,23 +39,48 @@ export default function DashboardPage() {
3439
<div style={{ color: "#666", fontSize: 12 }}>
3540
{result?.pipeline_name} · Generated {result ? new Date(result.created_at).toLocaleString() : "—"}
3641
</div>
37-
<pre style={{ maxHeight: 280, overflow: "auto", background: "#f6f6f6", padding: 12 }}>
42+
<pre style={{ maxHeight: 280, overflow: "auto", background: "#1e1c1cff", padding: 12 }}>
3843
{result?.generated_yaml ?? "No pipeline generated yet."}
3944
</pre>
4045
</div>
4146

4247
<div>
4348
<button
4449
disabled={running}
45-
onClick={() => startDeploy({ repo: repo!, env: cfg.env })}
50+
onClick={() => {
51+
const repoFullName = result?.repo || repo;
52+
const yaml = result?.generated_yaml;
53+
const branch = result?.branch || "main";
54+
const environment = cfg.env || "dev";
55+
const provider = "aws";
56+
const path = `.github/workflows/${environment}-deploy.yml`;
57+
58+
console.log("Deploy button clicked with payload:", {
59+
repoFullName,
60+
branch,
61+
env: environment,
62+
yaml: yaml ? yaml.slice(0, 100) + "..." : "No YAML",
63+
provider,
64+
path,
65+
});
66+
67+
startDeploy({
68+
repoFullName,
69+
branch,
70+
env: environment,
71+
yaml,
72+
provider,
73+
path,
74+
});
75+
}}
4676
>
47-
{running ? "Deploying…" : "Deploy to AWS"}
77+
{running ? "Committing…" : "Commit to GitHub"}
4878
</button>
4979
{running && <button onClick={stop} style={{ marginLeft: 8 }}>Stop</button>}
5080
</div>
5181

5282
<div>
53-
<strong>Logs</strong>
83+
<strong>Commit Logs</strong>
5484
<div style={{ height: 280, overflow: "auto", background: "#111", color: "#ddd", padding: 12, fontFamily: "monospace", fontSize: 12 }}>
5585
{events.length === 0 ? "No logs yet." :
5686
events.map((e, i) => (

client/src/pages/DeployPage.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,29 @@ export default function DeployPage() {
1010
// loadRoles();
1111
}, []);
1212

13+
function startCommit() {
14+
// Placeholder function to start commit process
15+
console.log("Commit to GitHub started");
16+
}
17+
1318
return (
1419
<div className="p-6 max-w-2xl mx-auto space-y-4">
20+
<h2 className="text-lg font-semibold">Commit Workflow to GitHub</h2>
1521
{!repo || !branch ? (
1622
<p className="text-sm text-orange-700">Pick a repo/branch on Connect first.</p>
1723
) : (
18-
<p className="text-sm opacity-80">Deploying <strong>{repo}</strong> @ <strong>{branch}</strong></p>
24+
<>
25+
<p className="text-sm opacity-80">Committing workflow to <strong>GitHub</strong> for <strong>{repo}</strong> @ <strong>{branch}</strong></p>
26+
<button
27+
className="mt-2 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
28+
onClick={startCommit}
29+
>
30+
Commit to GitHub
31+
</button>
32+
</>
1933
)}
2034

21-
{/* role picker + open PR button go here, wired to your deploy store */}
35+
{/* role picker + commit to GitHub button go here, wired to your deploy store */}
2236
</div>
2337
);
2438
}

0 commit comments

Comments
 (0)