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
29 changes: 29 additions & 0 deletions server/lib/pipelineVersions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import crypto from 'crypto';
import { query } from '../db.js';

export async function savePipelineVersion({
userId,
repoFullName,
branch,
workflowPath,
yaml,
source = 'pipeline_commit',
}) {
if (!repoFullName || !branch || !workflowPath || !yaml) {
throw new Error('Missing required fields for savePipelineVersion');
}

const hash = crypto.createHash('sha256').update(yaml, 'utf-8').digest('hex');
console.log(`hash: ${hash}`);

const rows = await query(
`
INSERT INTO pipeline_versions
(user_id, repo_full_name, branch, workflow_path, yaml, yaml_hash, source)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *
`,
[userId ?? null, repoFullName, branch, workflowPath, yaml, hash, source]
);
return rows[0];
}
21 changes: 11 additions & 10 deletions server/routes/deployments.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,18 +354,19 @@ router.post('/dispatch', requireSession, async (req, res) => {
await query(
`
INSERT INTO deployment_logs
(user_id, provider, repo_full_name, environment, branch,
status, started_at, summary, metadata)
VALUES ($1, $2, $3, $4, $5,
'queued', NOW(), $6, $7::jsonb);
(user_id, provider, repo_full_name, environment, branch, action,
status, started_at, summary, metadata)
VALUES ($1, $2, $3, $4, $5, $6,
'queued', NOW(), $7, $8::jsonb)
`,
[
userId, // user_id
'github_actions', // provider
repoFullName, // repo_full_name
inputs?.environment ?? 'dev', // environment
ref, // branch
`Dispatch ${workflow} via API`, // summary
userId,
'github_actions',
repoFullName,
inputs?.environment ?? 'dev',
ref,
'dispatch',
`Dispatch ${workflow} via API`,
JSON.stringify({
workflow,
ref,
Expand Down
106 changes: 106 additions & 0 deletions server/routes/pipelineCommit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Router } from 'express';
import { requireSession } from '../lib/requireSession.js';
import { getGithubAccessTokenForUser } from '../lib/github-token.js';
import { upsertWorkflowFile } from '../tools/github_adapter.js';
import { query } from '../db.js';
import { savePipelineVersion } from '../lib/pipelineVersions.js';

const router = Router();

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

await query(
`
INSERT INTO deployment_logs
(user_id, provider, repo_full_name, environment, branch, action,
status, started_at, summary, metadata)
VALUES ($1, $2, $3, $4, $5, $6,
'success', NOW(), $7, $8::jsonb);
`,
[
userId, // user_id
'github_actions', // provider (or 'pipeline' if you prefer)
repoFullName, // repo_full_name
'global', // environment
branchName, // branch
'pipeline_commit', // action
`Committed workflow ${workflowPath} via OSP`, // summary
JSON.stringify({
workflow_path: workflowPath,
branch: branchName,
commit_sha: result?.commit?.sha || null,
commit_url: result?.commit?.html_url || null,
source: 'pipeline_commit',
}),
]
);

// Save a version of the pipelin YAML for history
await savePipelineVersion({
userId,
repoFullName,
branch: branchName,
workflowPath,
yaml,
source: 'pipeline_commit',
});

return res.status(201).json({
ok: true,
message: 'Workflow committed successfully',
Expand All @@ -64,4 +102,72 @@ router.post('/pipeline_commit', requireSession, async (req, res) => {
}
});

/**
* GET /mcp/v1/pipeline_history
* Query params:
* repoFullName (required) - "owner/repo"
* branch (optional) - default "main"
* path (optional) - default ".github/workflows/ci.yml"
* limit (optional) - default 20
*
* Example:
* GET /mcp/v1/pipeline_history?repoFullName=lorencDedaj/NeatNest&branch=main
*/

router.get('/pipeline_history', requireSession, async (req, res) => {
try {
const { repoFullName, branch, path, limit } = req.query || {};

if (!repoFullName) {
return res
.status(400)
.json({ error: 'repoFUllName query param is required' });
}

const userId = req.user?.user_id;
if (!userId) {
return res
.status(400)
.json({ error: 'userId session missing or invalid' });
}

const branchName = branch || 'main';
const workflowPath = path || '.github/workflows/ci.yml';
const lim = Math.min(parseInt(limit || '20', 10) || 20, 100);

const rows = await query(
`
select
id,
user_id,
repo_full_name,
branch,
workflow_path,
yaml,
yaml_hash,
source,
created_at
from pipeline_versions
where repo_full_name = $1
and branch = $2
and workflow_path = $3
order by created_at desc
limit $4;
`,
[repoFullName, branchName, workflowPath, lim]
);

return res.json({
ok: true,
versions: rows,
});
} catch (err) {
console.error('[pipeline_history] error: ', err);
const status = err.status || 500;
return res.status(status).json({
error: err.message || 'Failed to fetch the pipeline commit history',
});
}
});

export default router;