Skip to content

Commit 41a88ea

Browse files
committed
implement pipeline_history endpoint
1 parent 759d6b4 commit 41a88ea

File tree

3 files changed

+146
-10
lines changed

3 files changed

+146
-10
lines changed

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

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

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

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

0 commit comments

Comments
 (0)