summaryrefslogtreecommitdiffhomepage
path: root/script/beta.ts
blob: a17f02a0913f4a5636e112304b85b5128d9cadbd (plain)
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/usr/bin/env bun

import { Script } from "@opencode-ai/script"

interface PR {
  number: number
  title: string
  author: { login: string }
}

interface RunResult {
  exitCode: number
  stdout: string
  stderr: string
}

async function main() {
  console.log("Fetching open PRs from team members...")

  const allPrs: PR[] = []
  for (const member of Script.team) {
    const result = await $`gh pr list --state open --author ${member} --json number,title,author --limit 100`.nothrow()
    if (result.exitCode !== 0) continue
    const memberPrs: PR[] = JSON.parse(result.stdout)
    allPrs.push(...memberPrs)
  }

  const seen = new Set<number>()
  const prs = allPrs.filter((pr) => {
    if (seen.has(pr.number)) return false
    seen.add(pr.number)
    return true
  })

  console.log(`Found ${prs.length} open PRs from team members`)

  if (prs.length === 0) {
    console.log("No team PRs to merge")
    return
  }

  console.log("Fetching latest dev branch...")
  const fetchDev = await $`git fetch origin dev`.nothrow()
  if (fetchDev.exitCode !== 0) {
    throw new Error(`Failed to fetch dev branch: ${fetchDev.stderr}`)
  }

  console.log("Checking out beta branch...")
  const checkoutBeta = await $`git checkout -B beta origin/dev`.nothrow()
  if (checkoutBeta.exitCode !== 0) {
    throw new Error(`Failed to checkout beta branch: ${checkoutBeta.stderr}`)
  }

  const applied: number[] = []

  for (const pr of prs) {
    console.log(`\nProcessing PR #${pr.number}: ${pr.title}`)

    console.log("  Fetching PR head...")
    const fetch = await run(["git", "fetch", "origin", `pull/${pr.number}/head:pr/${pr.number}`])
    if (fetch.exitCode !== 0) {
      throw new Error(`Failed to fetch PR #${pr.number}: ${fetch.stderr}`)
    }

    console.log("  Merging...")
    const merge = await run(["git", "merge", "--no-commit", "--no-ff", `pr/${pr.number}`])
    if (merge.exitCode !== 0) {
      console.log("  Failed to merge (conflicts)")
      await $`git merge --abort`.nothrow()
      await $`git checkout -- .`.nothrow()
      await $`git clean -fd`.nothrow()
      throw new Error(`Failed to merge PR #${pr.number}: Has conflicts`)
    }

    const mergeHead = await $`git rev-parse -q --verify MERGE_HEAD`.nothrow()
    if (mergeHead.exitCode !== 0) {
      console.log("  No changes, skipping")
      continue
    }

    const add = await $`git add -A`.nothrow()
    if (add.exitCode !== 0) {
      throw new Error(`Failed to stage changes for PR #${pr.number}`)
    }

    const commitMsg = `Apply PR #${pr.number}: ${pr.title}`
    const commit = await run(["git", "commit", "-m", commitMsg])
    if (commit.exitCode !== 0) {
      throw new Error(`Failed to commit PR #${pr.number}: ${commit.stderr}`)
    }

    console.log("  Applied successfully")
    applied.push(pr.number)
  }

  console.log("\n--- Summary ---")
  console.log(`Applied: ${applied.length} PRs`)
  applied.forEach((num) => console.log(`  - PR #${num}`))

  console.log("\nForce pushing beta branch...")
  const push = await $`git push origin beta --force --no-verify`.nothrow()
  if (push.exitCode !== 0) {
    throw new Error(`Failed to push beta branch: ${push.stderr}`)
  }

  console.log("Successfully synced beta branch")
}

main().catch((err) => {
  console.error("Error:", err)
  process.exit(1)
})

async function run(args: string[], stdin?: Uint8Array): Promise<RunResult> {
  const proc = Bun.spawn(args, {
    stdin: stdin ?? "inherit",
    stdout: "pipe",
    stderr: "pipe",
  })
  const exitCode = await proc.exited
  const stdout = await new Response(proc.stdout).text()
  const stderr = await new Response(proc.stderr).text()
  return { exitCode, stdout, stderr }
}

function $(strings: TemplateStringsArray, ...values: unknown[]) {
  const cmd = strings.reduce((acc, str, i) => acc + str + (values[i] ?? ""), "")
  return {
    async nothrow() {
      const proc = Bun.spawn(cmd.split(" "), {
        stdout: "pipe",
        stderr: "pipe",
      })
      const exitCode = await proc.exited
      const stdout = await new Response(proc.stdout).text()
      const stderr = await new Response(proc.stderr).text()
      return { exitCode, stdout, stderr }
    },
  }
}