/ opencodehub / src / db / schema / merge-queue.ts
Swadhin Biswas Feature/ai code reviewer (#80) · 8/17/2026
9740d3d
115 lines · 4.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import { relations } from "drizzle-orm";
import { boolean, index, integer, pgTable, text, timestamp } from "drizzle-orm/pg-core";
import { pullRequests } from "./pull-requests";
import { repositories } from "./repositories";
import { users } from "./users";

export const mergeQueue = pgTable("merge_queue", {
    id: text("id").primaryKey(),
    repositoryId: text("repository_id")
        .notNull()
        .references(() => repositories.id, { onDelete: "cascade" }),
    pullRequestId: text("pull_request_id")
        .notNull()
        .references(() => pullRequests.id, { onDelete: "cascade" }),
    stackId: text("stack_id"), // Reference to stacked PR group

    // Queue management
    status: text("status").notNull().default("pending"), // pending, merging, merged, failed
    priority: integer("priority").default(0), // Higher = more important
    position: integer("position").default(0), // Queue position

    // CI status
    ciStatus: text("ci_status").default("pending"), // pending, running, passed, failed

    // Merge configuration
    mergeMethod: text("merge_method").default("merge"), // merge, squash, rebase
    deleteOnMerge: boolean("delete_on_merge").default(true),

    // Execution details
    executionBranch: text("execution_branch"), // mq/pr-123-attempt-1
    attemptCount: integer("attempt_count").default(0),
    lastAttemptAt: timestamp("last_attempt_at"),

    // Tracking
    addedById: text("added_by_id").references(() => users.id),
    addedAt: timestamp("added_at").notNull().defaultNow(),
    queuedAt: timestamp("queued_at").notNull().defaultNow(), // Alias for addedAt in some contexts
    startedAt: timestamp("started_at"),
    completedAt: timestamp("completed_at"),
    failureReason: text("failure_reason"),
  },
  (t) => ({
    repoIdx: index("merge_queue_repo_idx").on(t.repositoryId),
    statusIdx: index("merge_queue_status_idx").on(t.status),
    prIdx: index("merge_queue_pr_idx").on(t.pullRequestId),
  }),
);

// Alias for backwards compatibility
export const mergeQueueItems = mergeQueue;

export const mergeQueueRelations = relations(mergeQueue, ({ one, many }) => ({
    repository: one(repositories, {
        fields: [mergeQueue.repositoryId],
        references: [repositories.id],
    }),
    pullRequest: one(pullRequests, {
        fields: [mergeQueue.pullRequestId],
        references: [pullRequests.id],
    }),
    addedBy: one(users, {
        fields: [mergeQueue.addedById],
        references: [users.id],
    }),
    speculativeRuns: many(mergeQueueSpeculativeRuns),
}));

// Track speculative builds for batched merge queue processing
export const mergeQueueSpeculativeRuns = pgTable("merge_queue_speculative_runs", {
    id: text("id").primaryKey(),
    repositoryId: text("repository_id")
        .notNull()
        .references(() => repositories.id, { onDelete: "cascade" }),

    // The PRs included in this speculative run (comma-separated IDs)
    pullRequestIds: text("pull_request_ids").notNull(),

    // Branch created for this speculative build
    branchName: text("branch_name").notNull(),
    baseBranch: text("base_branch").notNull(),

    // Status tracking
    status: text("status").notNull().default("pending"), // pending, running, passed, failed, cancelled
    ciUrl: text("ci_url"), // Link to CI run

    // Timing
    createdAt: timestamp("created_at").notNull().defaultNow(),
    startedAt: timestamp("started_at"),
    completedAt: timestamp("completed_at"),

    // Results
    failureReason: text("failure_reason"),
    commitSha: text("commit_sha"), // SHA of the merged speculative commit
});

export const mergeQueueSpeculativeRunsRelations = relations(mergeQueueSpeculativeRuns, ({ one }) => ({
    repository: one(repositories, {
        fields: [mergeQueueSpeculativeRuns.repositoryId],
        references: [repositories.id],
    }),
}));

// Alias for backwards compatibility
export const mergeQueueItemsRelations = mergeQueueRelations;

export type MergeQueueEntry = typeof mergeQueue.$inferSelect;
export type NewMergeQueueEntry = typeof mergeQueue.$inferInsert;
export type SpeculativeRun = typeof mergeQueueSpeculativeRuns.$inferSelect;
export type NewSpeculativeRun = typeof mergeQueueSpeculativeRuns.$inferInsert;

// Backwards compatibility aliases
export type MergeQueueItem = MergeQueueEntry;
export type NewMergeQueueItem = NewMergeQueueEntry;