237db28
commit 237db28
6 files changed
+135 -8
Showing 6 changed files+135-8
▼.github/workflows/deploy.yml
+28-0
| @@ -0,0 +1,28 @@ | ||
1 | +name: Deploy to Vercel | |
2 | + | |
3 | +on: | |
4 | + push: | |
5 | + branches: | |
6 | + - main | |
7 | + | |
8 | +env: | |
9 | + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} | |
10 | + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} | |
11 | + | |
12 | +jobs: | |
13 | + deploy: | |
14 | + runs-on: ubuntu-latest | |
15 | + steps: | |
16 | + - uses: actions/checkout@v4 | |
17 | + | |
18 | + - name: Install Vercel CLI | |
19 | + run: npm install --global vercel@latest | |
20 | + | |
21 | + - name: Pull Vercel Environment Information | |
22 | + run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} | |
23 | + | |
24 | + - name: Build Project Artifacts | |
25 | + run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} | |
26 | + | |
27 | + - name: Deploy Project Artifacts to Vercel | |
28 | + run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} | |
0 | 29 | |
▼package-lock.json
+1-1
| @@ -118,7 +118,7 @@ | ||
118 | 118 | "vitest": "^4.0.15" |
119 | 119 | }, |
120 | 120 | "engines": { |
121 | - "node": ">=18.0.0" | |
121 | + "node": ">=20.0.0" | |
122 | 122 | } |
123 | 123 | }, |
124 | 124 | "node_modules/@alloc/quick-lru": { |
125 | 125 | |
▼package.json
+2-2
| @@ -140,6 +140,6 @@ | ||
140 | 140 | "vitest": "^4.0.15" |
141 | 141 | }, |
142 | 142 | "engines": { |
143 | - "node": ">=18.0.0" | |
143 | + "node": ">=20.0.0" | |
144 | 144 | } |
145 | -} | |
145 | +} | |
146 | 146 | |
▼src/db/schema/pull-requests.ts
+52-0
| @@ -235,6 +235,58 @@ export const pullRequestCommentsRelations = relations( | ||
235 | 235 | }) |
236 | 236 | ); |
237 | 237 | |
238 | +export const pullRequestLabelsRelations = relations( | |
239 | + pullRequestLabels, | |
240 | + ({ one }) => ({ | |
241 | + pullRequest: one(pullRequests, { | |
242 | + fields: [pullRequestLabels.pullRequestId], | |
243 | + references: [pullRequests.id], | |
244 | + }), | |
245 | + label: one(labels, { | |
246 | + fields: [pullRequestLabels.labelId], | |
247 | + references: [labels.id], | |
248 | + }), | |
249 | + }) | |
250 | +); | |
251 | + | |
252 | +export const pullRequestAssigneesRelations = relations( | |
253 | + pullRequestAssignees, | |
254 | + ({ one }) => ({ | |
255 | + pullRequest: one(pullRequests, { | |
256 | + fields: [pullRequestAssignees.pullRequestId], | |
257 | + references: [pullRequests.id], | |
258 | + }), | |
259 | + user: one(users, { | |
260 | + fields: [pullRequestAssignees.userId], | |
261 | + references: [users.id], | |
262 | + }), | |
263 | + }) | |
264 | +); | |
265 | + | |
266 | +export const pullRequestReviewersRelations = relations( | |
267 | + pullRequestReviewers, | |
268 | + ({ one }) => ({ | |
269 | + pullRequest: one(pullRequests, { | |
270 | + fields: [pullRequestReviewers.pullRequestId], | |
271 | + references: [pullRequests.id], | |
272 | + }), | |
273 | + user: one(users, { | |
274 | + fields: [pullRequestReviewers.userId], | |
275 | + references: [users.id], | |
276 | + }), | |
277 | + }) | |
278 | +); | |
279 | + | |
280 | +export const pullRequestChecksRelations = relations( | |
281 | + pullRequestChecks, | |
282 | + ({ one }) => ({ | |
283 | + pullRequest: one(pullRequests, { | |
284 | + fields: [pullRequestChecks.pullRequestId], | |
285 | + references: [pullRequests.id], | |
286 | + }), | |
287 | + }) | |
288 | +); | |
289 | + | |
238 | 290 | // Types |
239 | 291 | export type PullRequest = typeof pullRequests.$inferSelect; |
240 | 292 | export type NewPullRequest = typeof pullRequests.$inferInsert; |
241 | 293 | |
▼src/lib/git.ts
+49-2
| @@ -222,6 +222,35 @@ export function getGit(repoPath: string): SimpleGit { | ||
222 | 222 | return simpleGit(options); |
223 | 223 | } |
224 | 224 | |
225 | +/** | |
226 | + * Check if a repository is empty (has no commits) | |
227 | + */ | |
228 | +export async function isRepoEmpty(repoPath: string): Promise<boolean> { | |
229 | + const git = getGit(repoPath); | |
230 | + try { | |
231 | + // Try to get HEAD ref - if it fails, repo is empty | |
232 | + await git.raw(["rev-parse", "HEAD"]); | |
233 | + return false; | |
234 | + } catch { | |
235 | + return true; | |
236 | + } | |
237 | +} | |
238 | + | |
239 | +/** | |
240 | + * Get the actual default branch of a repository (from git, not database) | |
241 | + * Returns null if repository is empty | |
242 | + */ | |
243 | +export async function getActualDefaultBranch(repoPath: string): Promise<string | null> { | |
244 | + const git = getGit(repoPath); | |
245 | + try { | |
246 | + const headRef = await git.raw(["symbolic-ref", "--short", "HEAD"]); | |
247 | + return headRef.trim(); | |
248 | + } catch { | |
249 | + // Repository might be empty or HEAD detached | |
250 | + return null; | |
251 | + } | |
252 | +} | |
253 | + | |
225 | 254 | /** |
226 | 255 | * List files in a directory at a specific ref |
227 | 256 | */ |
| @@ -233,6 +262,11 @@ export async function listFiles( | ||
233 | 262 | const git = getGit(repoPath); |
234 | 263 | |
235 | 264 | try { |
265 | + // Check if repository is empty first | |
266 | + if (await isRepoEmpty(repoPath)) { | |
267 | + return []; | |
268 | + } | |
269 | + | |
236 | 270 | // Use ls-tree to get files |
237 | 271 | // Append / to path to list contents of directory, otherwise it lists the directory entry itself |
238 | 272 | const targetPath = path ? (path.endsWith("/") ? path : `${path}/`) : "."; |
| @@ -277,7 +311,11 @@ export async function listFiles( | ||
277 | 311 | |
278 | 312 | return entries; |
279 | 313 | } catch (error) { |
280 | - logger.error({ err: error }, "Error listing files"); | |
314 | + // Only log if it's not an empty repo issue | |
315 | + const errorMessage = error instanceof Error ? error.message : String(error); | |
316 | + if (!errorMessage.includes("Not a valid object name") && !errorMessage.includes("unknown revision")) { | |
317 | + logger.error({ err: error }, "Error listing files"); | |
318 | + } | |
281 | 319 | return []; |
282 | 320 | } |
283 | 321 | } |
| @@ -389,6 +427,11 @@ export async function getCommits( | ||
389 | 427 | const { ref = "HEAD", path, limit = 30, skip = 0 } = options; |
390 | 428 | |
391 | 429 | try { |
430 | + // Check if repository is empty first | |
431 | + if (await isRepoEmpty(repoPath)) { | |
432 | + return []; | |
433 | + } | |
434 | + | |
392 | 435 | const args = [ |
393 | 436 | "log", |
394 | 437 | "--format=%H|%s|%b|%an|%ae|%ai|%cn|%ce|%ci|%P|%G?|%GK|%GS", |
| @@ -442,7 +485,11 @@ export async function getCommits( | ||
442 | 485 | |
443 | 486 | return commits; |
444 | 487 | } catch (error) { |
445 | - logger.error({ err: error }, "Error getting commits"); | |
488 | + // Only log if it's not an empty repo issue | |
489 | + const errorMessage = error instanceof Error ? error.message : String(error); | |
490 | + if (!errorMessage.includes("unknown revision") && !errorMessage.includes("ambiguous argument")) { | |
491 | + logger.error({ err: error }, "Error getting commits"); | |
492 | + } | |
446 | 493 | return []; |
447 | 494 | } |
448 | 495 | } |
449 | 496 | |
▼src/pages/api/repos/[owner]/[repo]/index.ts
+3-3
| @@ -57,7 +57,7 @@ export const GET: APIRoute = async ({ params, request }) => { | ||
57 | 57 | const repository = await db.query.repositories.findFirst({ |
58 | 58 | where: and( |
59 | 59 | eq(repositories.ownerId, ownerUser.id), |
60 | - eq(repositories.slug, repo) | |
60 | + eq(repositories.name, repo) | |
61 | 61 | ), |
62 | 62 | with: { |
63 | 63 | owner: { |
| @@ -170,7 +170,7 @@ export const PATCH: APIRoute = async ({ params, request }) => { | ||
170 | 170 | const repository = await db.query.repositories.findFirst({ |
171 | 171 | where: and( |
172 | 172 | eq(repositories.ownerId, ownerUser.id), |
173 | - eq(repositories.slug, repo) | |
173 | + eq(repositories.name, repo) | |
174 | 174 | ), |
175 | 175 | }); |
176 | 176 | |
| @@ -277,7 +277,7 @@ export const DELETE: APIRoute = async ({ params, request }) => { | ||
277 | 277 | const repository = await db.query.repositories.findFirst({ |
278 | 278 | where: and( |
279 | 279 | eq(repositories.ownerId, ownerUser.id), |
280 | - eq(repositories.slug, repo) | |
280 | + eq(repositories.name, repo) | |
281 | 281 | ), |
282 | 282 | }); |
283 | 283 | |
284 | 284 | |