chore: update src/lib/pr-dependencies.ts

swadhinbiswas committed 8 months ago
93b5e3c
1 parent 556e308
commit 93b5e3c
1 file changed
+88 -5
Showing 1 changed file+88-5
▼src/lib/pr-dependencies.ts
+88-5
6
6
import { getDatabase, schema } from "@/db";
7
7
import { eq, and, inArray } from "drizzle-orm";
8
8
import { logger } from "./logger";
9
+import { getChangedFiles } from "./git";
9
10
10
11
export interface PRDependency {
11
12
prId: string;
88
89
export async function detectFileDependencies(
89
90
repositoryId: string
90
91
): Promise<{ conflicting: { pr1: string; pr2: string; files: string[] }[] }> {
91
- // Note: This would require accessing git diff data
92
- // For now, return empty - implementation would need git integration
93
- logger.info({ repositoryId }, "File dependency detection not yet implemented");
94
- return { conflicting: [] };
92
+ const db = getDatabase();
93
+
94
+ const repo = await db.query.repositories.findFirst({
95
+ where: eq(schema.repositories.id, repositoryId),
96
+ });
97
+
98
+ if (!repo?.diskPath) {
99
+ logger.warn({ repositoryId }, "Skipping file dependency detection: repository disk path not available");
100
+ return { conflicting: [] };
101
+ }
102
+
103
+ const prs = await db.query.pullRequests.findMany({
104
+ where: and(
105
+ eq(schema.pullRequests.repositoryId, repositoryId),
106
+ eq(schema.pullRequests.state, "open")
107
+ ),
108
+ });
109
+
110
+ if (prs.length < 2) {
111
+ return { conflicting: [] };
112
+ }
113
+
114
+ const filesByPrId = new Map<string, Set<string>>();
115
+ for (const pr of prs) {
116
+ try {
117
+ const files = await getChangedFiles(repo.diskPath, pr.baseBranch, pr.headBranch);
118
+ filesByPrId.set(pr.id, new Set(files));
119
+ } catch (error) {
120
+ logger.warn({ repositoryId, prId: pr.id, error }, "Failed to load PR changed files for dependency detection");
121
+ filesByPrId.set(pr.id, new Set<string>());
122
+ }
123
+ }
124
+
125
+ const conflicting: { pr1: string; pr2: string; files: string[] }[] = [];
126
+
127
+ for (let i = 0; i < prs.length; i++) {
128
+ for (let j = i + 1; j < prs.length; j++) {
129
+ const left = prs[i];
130
+ const right = prs[j];
131
+ const leftFiles = filesByPrId.get(left.id) || new Set<string>();
132
+ const rightFiles = filesByPrId.get(right.id) || new Set<string>();
133
+
134
+ const overlap: string[] = [];
135
+ for (const file of leftFiles) {
136
+ if (rightFiles.has(file)) overlap.push(file);
137
+ }
138
+
139
+ if (overlap.length > 0) {
140
+ conflicting.push({
141
+ pr1: left.id,
142
+ pr2: right.id,
143
+ files: overlap.sort(),
144
+ });
145
+ }
146
+ }
147
+ }
148
+
149
+ return { conflicting };
95
150
}
96
151
97
152
/**
98
153
* Get dependency graph for visualization
99
154
*/
100
155
export async function getDependencyGraph(repositoryId: string): Promise<DependencyGraph> {
101
- return detectBranchDependencies(repositoryId);
156
+ const [branchGraph, fileDeps] = await Promise.all([
157
+ detectBranchDependencies(repositoryId),
158
+ detectFileDependencies(repositoryId),
159
+ ]);
160
+
161
+ const edges = [...branchGraph.edges];
162
+ for (const conflict of fileDeps.conflicting) {
163
+ edges.push({
164
+ from: conflict.pr1,
165
+ to: conflict.pr2,
166
+ type: "files",
167
+ });
168
+ }
169
+
170
+ const conflictByPr = new Map<string, Set<string>>();
171
+ for (const conflict of fileDeps.conflicting) {
172
+ if (!conflictByPr.has(conflict.pr1)) conflictByPr.set(conflict.pr1, new Set<string>());
173
+ if (!conflictByPr.has(conflict.pr2)) conflictByPr.set(conflict.pr2, new Set<string>());
174
+ conflictByPr.get(conflict.pr1)!.add(conflict.pr2);
175
+ conflictByPr.get(conflict.pr2)!.add(conflict.pr1);
176
+ }
177
+
178
+ const nodes = branchGraph.nodes.map((node) => ({
179
+ ...node,
180
+ blockedBy: Array.from(new Set([...node.blockedBy, ...(conflictByPr.get(node.prId) || [])])),
181
+ dependencyType: (conflictByPr.get(node.prId)?.size ? "files" : node.dependencyType) as PRDependency["dependencyType"],
182
+ }));
183
+
184
+ return { nodes, edges };
102
185
}
103
186
104
187
/**
105
188