Skip to content

Commit 9d902aa

Browse files
authored
Merge pull request #28 from oslabs-beta/lorenc-ci
fix the CAN NOT GET bug
2 parents 43730bc + e23aab4 commit 9d902aa

File tree

6 files changed

+142
-76
lines changed

6 files changed

+142
-76
lines changed

.dockerignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
node_modules
2+
npm-debug.log
3+
Dockerfile
4+
Dockerfile.jenkins
5+
docker-compose*.yml
6+
.git
7+
.gitignore
8+
.env
9+
.vscode
10+
dist

Dockerfile

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,17 @@
1-
# Jenkins LTS (with JDK 17). The current LTS line (>= 2.492.3) meets the MCP plugin minimum requirement.
2-
FROM jenkins/jenkins:lts-jdk17
3-
4-
USER root
5-
6-
# Install base tools (git, curl, certificates).
7-
# If you use dedicated Jenkins agents, also install git inside your agent images.
8-
RUN apt-get update && apt-get install -y --no-install-recommends \
9-
git curl ca-certificates && \
10-
rm -rf /var/lib/apt/lists/*
11-
12-
# Switch back to jenkins user
13-
USER jenkins
14-
15-
# Preinstall plugins:
16-
# MCP Server, Git, Git Client, GitHub integration, Pipeline, and Credentials
17-
# Note: jenkins-plugin-cli is included in the official Jenkins image.
18-
RUN jenkins-plugin-cli --plugins \
19-
mcp-server \
20-
git \
21-
git-client \
22-
github \
23-
github-branch-source \
24-
workflow-aggregator \
25-
credentials \
26-
ssh-credentials \
27-
configuration-as-code
28-
29-
# Expose ports
30-
EXPOSE 8080 50000
31-
32-
# (Optional) Jenkins startup parameters
33-
# Disable the setup wizard on first startup:
34-
# ENV JAVA_OPTS="-Djenkins.install.runSetupWizard=false"
35-
36-
# (Optional) Mount JCasC configuration file
37-
# ENV CASC_JENKINS_CONFIG=/var/jenkins_home/casc.yaml
1+
FROM node:20-alpine
2+
3+
WORKDIR /app
4+
5+
ENV NODE_ENV=production
6+
7+
ENV PORT=3000
8+
9+
COPY package*.json ./
10+
11+
RUN npm ci --omit=dev
12+
13+
COPY . .
14+
15+
EXPOSE 3000
16+
17+
CMD ["node", "server/server.js"]

Dockerfile.jenkins

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Jenkins LTS (with JDK 17). The current LTS line (>= 2.492.3) meets the MCP plugin minimum requirement.
2+
FROM jenkins/jenkins:lts-jdk17
3+
4+
USER root
5+
6+
# Install base tools (git, curl, certificates).
7+
# If you use dedicated Jenkins agents, also install git inside your agent images.
8+
RUN apt-get update && apt-get install -y --no-install-recommends \
9+
git curl ca-certificates && \
10+
rm -rf /var/lib/apt/lists/*
11+
12+
# Switch back to jenkins user
13+
USER jenkins
14+
15+
# Preinstall plugins:
16+
# MCP Server, Git, Git Client, GitHub integration, Pipeline, and Credentials
17+
# Note: jenkins-plugin-cli is included in the official Jenkins image.
18+
RUN jenkins-plugin-cli --plugins \
19+
mcp-server \
20+
git \
21+
git-client \
22+
github \
23+
github-branch-source \
24+
workflow-aggregator \
25+
credentials \
26+
ssh-credentials \
27+
configuration-as-code
28+
29+
# Expose ports
30+
EXPOSE 8080 50000
31+
32+
# (Optional) Jenkins startup parameters
33+
# Disable the setup wizard on first startup:
34+
# ENV JAVA_OPTS="-Djenkins.install.runSetupWizard=false"
35+
36+
# (Optional) Mount JCasC configuration file
37+
# ENV CASC_JENKINS_CONFIG=/var/jenkins_home/casc.yaml

server/agent/wizardAgent.js

Lines changed: 72 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
1-
import OpenAI from "openai";
2-
import dotenv from "dotenv";
3-
import fetch from "node-fetch";
1+
import OpenAI from 'openai';
2+
import dotenv from 'dotenv';
3+
import fetch from 'node-fetch';
44

55
dotenv.config();
6-
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
6+
//const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
7+
8+
// Construct the OpenAI client lazily so that the server does not shut down completely when we build the container
9+
let client = null;
10+
11+
function getClient() {
12+
if (!client) {
13+
const apiKey = process.env.OPENAI_API_KEY;
14+
if (!apiKey) {
15+
throw new Error('Missing OPENAI_API_KEY - cannot run Wizard Agent');
16+
}
17+
client = new OpenAI({ apiKey });
18+
}
19+
return client;
20+
}
721

822
// Helper: call MCP routes dynamically, with error handling
923
async function callMCPTool(tool, input, cookie) {
1024
try {
1125
const response = await fetch(`http://localhost:3000/mcp/v1/${tool}`, {
12-
method: "POST",
26+
method: 'POST',
1327
headers: {
1428
"Content-Type": "application/json",
1529
"Cookie": cookie || (process.env.MCP_SESSION_TOKEN ? `mcp_session=${process.env.MCP_SESSION_TOKEN}` : ""),
@@ -18,8 +32,8 @@ async function callMCPTool(tool, input, cookie) {
1832
});
1933
return await response.json();
2034
} catch (err) {
21-
console.warn("⚠️ MCP call failed:", err.message || err);
22-
return { error: "MCP server unreachable" };
35+
console.warn('⚠️ MCP call failed:', err.message || err);
36+
return { error: 'MCP server unreachable' };
2337
}
2438
}
2539

@@ -59,16 +73,18 @@ export async function runWizardAgent(userPrompt) {
5973
Never invent new template names. If unsure, default to "node_app".
6074
`;
6175

76+
const client = getClient();
77+
6278
const completion = await client.chat.completions.create({
63-
model: "gpt-4o-mini",
79+
model: 'gpt-4o-mini',
6480
messages: [
6581
{ role: "system", content: systemPrompt },
6682
{ role: "user", content: typeof userPrompt === "string" ? userPrompt : userPrompt.prompt },
6783
],
6884
});
6985

7086
const decision = completion.choices[0].message.content;
71-
console.log("\n🤖 Agent decided:", decision);
87+
console.log('\n🤖 Agent decided:', decision);
7288

7389
let agentMeta = {
7490
agent_decision: decision,
@@ -117,7 +133,7 @@ export async function runWizardAgent(userPrompt) {
117133
if (usernameMatch) payload.username = usernameMatch[1];
118134
if (userIdMatch) payload.user_id = userIdMatch[1];
119135
if (repoMatch) {
120-
const [username, repo] = repoMatch[1].split("/");
136+
const [username, repo] = repoMatch[1].split('/');
121137
payload.username = username;
122138
payload.repo = `${username}/${repo}`;
123139
}
@@ -132,12 +148,13 @@ export async function runWizardAgent(userPrompt) {
132148
};
133149
}
134150

135-
if (toolName === "pipeline_generator") {
151+
if (toolName === 'pipeline_generator') {
136152
if (!repo) {
137-
console.warn("⚠️ Missing repo context for pipeline generation.");
138-
return {
139-
success: false,
140-
error: "I couldn’t determine which repository you meant. Please specify it, e.g., 'generate pipeline for user/repo'."
153+
console.warn('⚠️ Missing repo context for pipeline generation.');
154+
return {
155+
success: false,
156+
error:
157+
"I couldn’t determine which repository you meant. Please specify it, e.g., 'generate pipeline for user/repo'.",
141158
};
142159
}
143160

@@ -154,29 +171,42 @@ export async function runWizardAgent(userPrompt) {
154171
console.log(`📦 Retrieved repo info from GitHub:`, repoInfo);
155172
}
156173
} catch (err) {
157-
console.warn("⚠️ Failed to fetch GitHub info before pipeline generation:", err.message);
174+
console.warn(
175+
'⚠️ Failed to fetch GitHub info before pipeline generation:',
176+
err.message
177+
);
158178
}
159179

160180
// Merge language or visibility into payload if available
161-
if (repoInfo?.language && !payload.language) payload.language = repoInfo.language.toLowerCase();
162-
if (repoInfo?.visibility && !payload.visibility) payload.visibility = repoInfo.visibility;
181+
if (repoInfo?.language && !payload.language)
182+
payload.language = repoInfo.language.toLowerCase();
183+
if (repoInfo?.visibility && !payload.visibility)
184+
payload.visibility = repoInfo.visibility;
163185

164186
// Infer template if still missing
165187
if (!payload.template) {
166-
if (repoInfo?.language?.toLowerCase().includes("javascript") || repoInfo?.language?.toLowerCase().includes("typescript") || /js|ts|node|javascript/i.test(repo)) {
167-
payload.template = "node_app";
168-
} else if (repoInfo?.language?.toLowerCase().includes("python") || /py|flask|django/i.test(repo)) {
169-
payload.template = "python_app";
188+
if (
189+
repoInfo?.language?.toLowerCase().includes('javascript') ||
190+
repoInfo?.language?.toLowerCase().includes('typescript') ||
191+
/js|ts|node|javascript/i.test(repo)
192+
) {
193+
payload.template = 'node_app';
194+
} else if (
195+
repoInfo?.language?.toLowerCase().includes('python') ||
196+
/py|flask|django/i.test(repo)
197+
) {
198+
payload.template = 'python_app';
170199
} else {
171-
payload.template = "container_service";
200+
payload.template = 'container_service';
172201
}
173202
console.log(`🪄 Inferred template: ${payload.template}`);
174203
}
175204

176205
// --- Auto-correct short template names ---
177-
if (payload.template === "node") payload.template = "node_app";
178-
if (payload.template === "python") payload.template = "python_app";
179-
if (payload.template === "container") payload.template = "container_service";
206+
if (payload.template === 'node') payload.template = 'node_app';
207+
if (payload.template === 'python') payload.template = 'python_app';
208+
if (payload.template === 'container')
209+
payload.template = 'container_service';
180210

181211
// --- Validate template against allowed values ---
182212
const allowedTemplates = ["node_app", "python_app", "container_service"];
@@ -193,9 +223,13 @@ export async function runWizardAgent(userPrompt) {
193223
}
194224

195225
// ✅ Ensure provider is valid before sending payload
196-
if (!payload.provider || !["aws", "jenkins"].includes(payload.provider)) {
226+
if (
227+
!payload.provider ||
228+
!['aws', 'jenkins'].includes(payload.provider)
229+
) {
197230
// Infer from repo visibility or fallback to AWS
198-
payload.provider = repoInfo?.visibility === "private" ? "jenkins" : "aws";
231+
payload.provider =
232+
repoInfo?.visibility === 'private' ? 'jenkins' : 'aws';
199233
console.log(`🧭 Inferred provider: ${payload.provider}`);
200234
}
201235

@@ -298,7 +332,7 @@ export async function runWizardAgent(userPrompt) {
298332
};
299333
}
300334

301-
if (toolName === "oidc_adapter") {
335+
if (toolName === 'oidc_adapter') {
302336
const payload = provider ? { provider } : {};
303337
agentMeta.tool_called = "oidc_adapter";
304338
const output = await callMCPTool("oidc_adapter", payload, cookie);
@@ -310,7 +344,7 @@ export async function runWizardAgent(userPrompt) {
310344
};
311345
}
312346

313-
if (toolName === "github_adapter") {
347+
if (toolName === 'github_adapter') {
314348
if (repo) {
315349
agentMeta.tool_called = "github_adapter";
316350
const output = await callMCPTool("github/info", { repo }, cookie);
@@ -321,10 +355,11 @@ export async function runWizardAgent(userPrompt) {
321355
tool_output: output
322356
};
323357
} else {
324-
console.warn("⚠️ Missing repo for GitHub info retrieval.");
325-
return {
326-
success: false,
327-
error: "Couldn’t determine which repository to fetch. Please include it in your request (e.g., 'tell me about user/repo')."
358+
console.warn('⚠️ Missing repo for GitHub info retrieval.');
359+
return {
360+
success: false,
361+
error:
362+
"Couldn’t determine which repository to fetch. Please include it in your request (e.g., 'tell me about user/repo').",
328363
};
329364
}
330365
}
@@ -341,10 +376,10 @@ export async function runWizardAgent(userPrompt) {
341376

342377
// Example local test (can comment out for production)
343378
if (process.argv[2]) {
344-
const input = process.argv.slice(2).join(" ");
379+
const input = process.argv.slice(2).join(' ');
345380
runWizardAgent(input)
346381
.then((res) => {
347-
console.log("\n📦 Tool Output:\n", JSON.stringify(res, null, 2));
382+
console.log('\n📦 Tool Output:\n', JSON.stringify(res, null, 2));
348383
})
349384
.catch(console.error);
350-
}
385+
}

server/routes/auth.github.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ const {
1919
JWT_SECRET,
2020
} = process.env;
2121

22+
// URL to redirect user to the apropiate endpoint after GitHub authentication success
23+
const FRONTEND_URL =
24+
process.env.FRONTEND_URL || 'http://localhost:5173/connect';
25+
2226
if (!GITHUB_CLIENT_ID || !GITHUB_CLIENT_SECRET || !GITHUB_OAUTH_REDIRECT_URI) {
2327
console.warn('[WARN] Missing GitHub OAuth env vars');
2428
}
@@ -158,7 +162,7 @@ router.get('/callback', async (req, res) => {
158162
// secure: true, // enable on HTTPS
159163
});
160164

161-
return res.redirect('/');
165+
return res.redirect(FRONTEND_URL);
162166
} catch (e) {
163167
console.error('[OAuth callback] error:', e);
164168
return res.status(500).send(`OAuth failed: ${e.message}`);

server/server.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ const UserBody = z.object({
7575

7676
// Create or upsert user by email
7777
app.post('/users', async (req, res) => {
78-
const parse = UserBody.safeParse(req.body); // love that you are doing this. great.
78+
const parse = UserBody.safeParse(req.body);
7979
if (!parse.success)
8080
return res.status(400).json({ error: parse.error.message });
8181
const { email, github_username } = parse.data;

0 commit comments

Comments
 (0)