beta.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. #!/usr/bin/env bun
  2. interface PR {
  3. number: number
  4. title: string
  5. }
  6. async function main() {
  7. console.log("Fetching open contributor PRs...")
  8. const prsResult = await $`gh pr list --label contributor --state open --json number,title --limit 100`.nothrow()
  9. if (prsResult.exitCode !== 0) {
  10. throw new Error(`Failed to fetch PRs: ${prsResult.stderr}`)
  11. }
  12. const prs: PR[] = JSON.parse(prsResult.stdout)
  13. console.log(`Found ${prs.length} open contributor PRs`)
  14. console.log("Fetching latest dev branch...")
  15. const fetchDev = await $`git fetch origin dev`.nothrow()
  16. if (fetchDev.exitCode !== 0) {
  17. throw new Error(`Failed to fetch dev branch: ${fetchDev.stderr}`)
  18. }
  19. console.log("Checking out beta branch...")
  20. const checkoutBeta = await $`git checkout -B beta origin/dev`.nothrow()
  21. if (checkoutBeta.exitCode !== 0) {
  22. throw new Error(`Failed to checkout beta branch: ${checkoutBeta.stderr}`)
  23. }
  24. const applied: number[] = []
  25. const skipped: Array<{ number: number; reason: string }> = []
  26. for (const pr of prs) {
  27. console.log(`\nProcessing PR #${pr.number}: ${pr.title}`)
  28. // Get the diff from GitHub
  29. console.log(` Getting diff...`)
  30. const diffResult = await $`gh pr diff ${pr.number}`.nothrow()
  31. if (diffResult.exitCode !== 0) {
  32. console.log(` Failed to get diff`)
  33. skipped.push({ number: pr.number, reason: `Failed to get diff: ${diffResult.stderr}` })
  34. continue
  35. }
  36. if (!diffResult.stdout.trim()) {
  37. console.log(` No changes, skipping`)
  38. skipped.push({ number: pr.number, reason: "No changes" })
  39. continue
  40. }
  41. // Try to apply the diff
  42. console.log(` Applying...`)
  43. const apply = await Bun.spawn(["git", "apply", "--3way"], {
  44. stdin: new TextEncoder().encode(diffResult.stdout),
  45. stdout: "pipe",
  46. stderr: "pipe",
  47. })
  48. const applyExit = await apply.exited
  49. const applyStderr = await Bun.readableStreamToText(apply.stderr)
  50. if (applyExit !== 0) {
  51. console.log(` Failed to apply (conflicts)`)
  52. await $`git checkout -- .`.nothrow()
  53. await $`git clean -fd`.nothrow()
  54. skipped.push({ number: pr.number, reason: "Has conflicts" })
  55. continue
  56. }
  57. // Stage and commit
  58. const add = await $`git add -A`.nothrow()
  59. if (add.exitCode !== 0) {
  60. console.log(` Failed to stage`)
  61. await $`git checkout -- .`.nothrow()
  62. await $`git clean -fd`.nothrow()
  63. skipped.push({ number: pr.number, reason: "Failed to stage" })
  64. continue
  65. }
  66. const commitMsg = `Apply PR #${pr.number}: ${pr.title}`
  67. const commit = await $`git commit -m ${commitMsg}`.nothrow()
  68. if (commit.exitCode !== 0) {
  69. console.log(` Failed to commit`)
  70. await $`git checkout -- .`.nothrow()
  71. await $`git clean -fd`.nothrow()
  72. skipped.push({ number: pr.number, reason: `Commit failed: ${commit.stderr}` })
  73. continue
  74. }
  75. console.log(` Applied successfully`)
  76. applied.push(pr.number)
  77. }
  78. console.log("\n--- Summary ---")
  79. console.log(`Applied: ${applied.length} PRs`)
  80. applied.forEach((num) => console.log(` - PR #${num}`))
  81. console.log(`Skipped: ${skipped.length} PRs`)
  82. skipped.forEach((x) => console.log(` - PR #${x.number}: ${x.reason}`))
  83. console.log("\nForce pushing beta branch...")
  84. const push = await $`git push origin beta --force --no-verify`.nothrow()
  85. if (push.exitCode !== 0) {
  86. throw new Error(`Failed to push beta branch: ${push.stderr}`)
  87. }
  88. console.log("Successfully synced beta branch")
  89. }
  90. main().catch((err) => {
  91. console.error("Error:", err)
  92. process.exit(1)
  93. })
  94. function $(strings: TemplateStringsArray, ...values: unknown[]) {
  95. const cmd = strings.reduce((acc, str, i) => acc + str + (values[i] ?? ""), "")
  96. return {
  97. async nothrow() {
  98. const proc = Bun.spawn(cmd.split(" "), {
  99. stdout: "pipe",
  100. stderr: "pipe",
  101. })
  102. const exitCode = await proc.exited
  103. const stdout = await new Response(proc.stdout).text()
  104. const stderr = await new Response(proc.stderr).text()
  105. return { exitCode, stdout, stderr }
  106. },
  107. }
  108. }