Skip to content

Commit 2628b35

Browse files
authored
Merge pull request #21 from oslabs-beta/lorenc-ci
implement pipeline_history endpoint
2 parents e2fcce7 + 41a88ea commit 2628b35

3 files changed

Lines changed: 146 additions & 10 deletions

File tree

server/lib/pipelineVersions.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import crypto from 'crypto';
2+
import { query } from '../db.js';
3+
4+
export async function savePipelineVersion({
5+
userId,
6+
repoFullName,
7+
branch,
8+
workflowPath,
9+
yaml,
10+
source = 'pipeline_commit',
11+
}) {
12+
if (!repoFullName || !branch || !workflowPath || !yaml) {
13+
throw new Error('Missing required fields for savePipelineVersion');
14+
}
15+
16+
const hash = crypto.createHash('sha256').update(yaml, 'utf-8').digest('hex');
17+
console.log(`hash: ${hash}`);
18+
19+
const rows = await query(
20+
`
21+
INSERT INTO pipeline_versions
22+
(user_id, repo_full_name, branch, workflow_path, yaml, yaml_hash, source)
23+
VALUES ($1, $2, $3, $4, $5, $6, $7)
24+
RETURNING *
25+
`,
26+
[userId ?? null, repoFullName, branch, workflowPath, yaml, hash, source]
27+
);
28+
return rows[0];
29+
}

server/routes/deployments.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -354,18 +354,19 @@ router.post('/dispatch', requireSession, async (req, res) => {
354354
await query(
355355
`
356356
INSERT INTO deployment_logs
357-
(user_id, provider, repo_full_name, environment, branch,
358-
status, started_at, summary, metadata)
359-
VALUES ($1, $2, $3, $4, $5,
360-
'queued', NOW(), $6, $7::jsonb);
357+
(user_id, provider, repo_full_name, environment, branch, action,
358+
status, started_at, summary, metadata)
359+
VALUES ($1, $2, $3, $4, $5, $6,
360+
'queued', NOW(), $7, $8::jsonb)
361361
`,
362362
[
363-
userId, // user_id
364-
'github_actions', // provider
365-
repoFullName, // repo_full_name
366-
inputs?.environment ?? 'dev', // environment
367-
ref, // branch
368-
`Dispatch ${workflow} via API`, // summary
363+
userId,
364+
'github_actions',
365+
repoFullName,
366+
inputs?.environment ?? 'dev',
367+
ref,
368+
'dispatch',
369+
`Dispatch ${workflow} via API`,
369370
JSON.stringify({
370371
workflow,
371372
ref,

server/routes/pipelineCommit.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { Router } from 'express';
22
import { requireSession } from '../lib/requireSession.js';
33
import { getGithubAccessTokenForUser } from '../lib/github-token.js';
44
import { upsertWorkflowFile } from '../tools/github_adapter.js';
5+
import { query } from '../db.js';
6+
import { savePipelineVersion } from '../lib/pipelineVersions.js';
57

68
const router = Router();
79

@@ -52,6 +54,42 @@ router.post('/pipeline_commit', requireSession, async (req, res) => {
5254
message: 'Add CI workflow via OSP',
5355
});
5456

57+
await query(
58+
`
59+
INSERT INTO deployment_logs
60+
(user_id, provider, repo_full_name, environment, branch, action,
61+
status, started_at, summary, metadata)
62+
VALUES ($1, $2, $3, $4, $5, $6,
63+
'success', NOW(), $7, $8::jsonb);
64+
`,
65+
[
66+
userId, // user_id
67+
'github_actions', // provider (or 'pipeline' if you prefer)
68+
repoFullName, // repo_full_name
69+
'global', // environment
70+
branchName, // branch
71+
'pipeline_commit', // action
72+
`Committed workflow ${workflowPath} via OSP`, // summary
73+
JSON.stringify({
74+
workflow_path: workflowPath,
75+
branch: branchName,
76+
commit_sha: result?.commit?.sha || null,
77+
commit_url: result?.commit?.html_url || null,
78+
source: 'pipeline_commit',
79+
}),
80+
]
81+
);
82+
83+
// Save a version of the pipelin YAML for history
84+
await savePipelineVersion({
85+
userId,
86+
repoFullName,
87+
branch: branchName,
88+
workflowPath,
89+
yaml,
90+
source: 'pipeline_commit',
91+
});
92+
5593
return res.status(201).json({
5694
ok: true,
5795
message: 'Workflow committed successfully',
@@ -66,4 +104,72 @@ router.post('/pipeline_commit', requireSession, async (req, res) => {
66104
}
67105
});
68106

107+
/**
108+
* GET /mcp/v1/pipeline_history
109+
* Query params:
110+
* repoFullName (required) - "owner/repo"
111+
* branch (optional) - default "main"
112+
* path (optional) - default ".github/workflows/ci.yml"
113+
* limit (optional) - default 20
114+
*
115+
* Example:
116+
* GET /mcp/v1/pipeline_history?repoFullName=lorencDedaj/NeatNest&branch=main
117+
*/
118+
119+
router.get('/pipeline_history', requireSession, async (req, res) => {
120+
try {
121+
const { repoFullName, branch, path, limit } = req.query || {};
122+
123+
if (!repoFullName) {
124+
return res
125+
.status(400)
126+
.json({ error: 'repoFUllName query param is required' });
127+
}
128+
129+
const userId = req.user?.user_id;
130+
if (!userId) {
131+
return res
132+
.status(400)
133+
.json({ error: 'userId session missing or invalid' });
134+
}
135+
136+
const branchName = branch || 'main';
137+
const workflowPath = path || '.github/workflows/ci.yml';
138+
const lim = Math.min(parseInt(limit || '20', 10) || 20, 100);
139+
140+
const rows = await query(
141+
`
142+
select
143+
id,
144+
user_id,
145+
repo_full_name,
146+
branch,
147+
workflow_path,
148+
yaml,
149+
yaml_hash,
150+
source,
151+
created_at
152+
from pipeline_versions
153+
where repo_full_name = $1
154+
and branch = $2
155+
and workflow_path = $3
156+
order by created_at desc
157+
limit $4;
158+
`,
159+
[repoFullName, branchName, workflowPath, lim]
160+
);
161+
162+
return res.json({
163+
ok: true,
164+
versions: rows,
165+
});
166+
} catch (err) {
167+
console.error('[pipeline_history] error: ', err);
168+
const status = err.status || 500;
169+
return res.status(status).json({
170+
error: err.message || 'Failed to fetch the pipeline commit history',
171+
});
172+
}
173+
});
174+
69175
export default router;

0 commit comments

Comments
 (0)