Просмотр исходного кода

feat: add beta branch sync workflow for contributor PRs (#11190)

Dax 2 недель назад
Родитель
Сommit
cd4075faf6

+ 34 - 0
.github/workflows/beta.yml

@@ -0,0 +1,34 @@
+name: beta
+
+on:
+  push:
+    branches: [dev]
+  pull_request:
+    types: [opened, synchronize, labeled, unlabeled]
+
+jobs:
+  sync:
+    if: |
+      github.event_name == 'push' || 
+      (github.event_name == 'pull_request' && 
+       contains(github.event.pull_request.labels.*.name, 'contributor'))
+    runs-on: blacksmith-4vcpu-ubuntu-2404
+    permissions:
+      contents: write
+      pull-requests: read
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Setup Bun
+        uses: ./.github/actions/setup-bun
+
+      - name: Configure Git
+        run: |
+          git config user.name "github-actions[bot]"
+          git config user.email "github-actions[bot]@users.noreply.github.com"
+
+      - name: Sync beta branch
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        run: bun script/beta.ts

+ 9 - 0
.github/workflows/nix-desktop.yml.disabled

@@ -1,6 +1,15 @@
 name: nix-desktop
 
 on:
+  push:
+    branches: [dev]
+    paths:
+      - "flake.nix"
+      - "flake.lock"
+      - "nix/**"
+      - "packages/app/**"
+      - "packages/desktop/**"
+      - ".github/workflows/nix-desktop.yml"
   pull_request:
     paths:
       - "flake.nix"

+ 1 - 0
.github/workflows/publish.yml

@@ -6,6 +6,7 @@ on:
     branches:
       - ci
       - dev
+      - beta
       - snapshot-*
   workflow_dispatch:
     inputs:

+ 127 - 0
script/beta.ts

@@ -0,0 +1,127 @@
+#!/usr/bin/env bun
+
+interface PR {
+  number: number
+  headRefName: string
+  headRefOid: string
+  createdAt: string
+  isDraft: boolean
+  title: string
+}
+
+async function main() {
+  console.log("Fetching open contributor PRs...")
+
+  const prsResult =
+    await $`gh pr list --label contributor --state open --json number,headRefName,headRefOid,createdAt,isDraft,title --limit 100`.nothrow()
+  if (prsResult.exitCode !== 0) {
+    throw new Error(`Failed to fetch PRs: ${prsResult.stderr}`)
+  }
+
+  const allPRs: PR[] = JSON.parse(prsResult.stdout)
+  const prs = allPRs.filter((pr) => !pr.isDraft)
+
+  console.log(`Found ${prs.length} open non-draft contributor PRs`)
+
+  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[] = []
+  const skipped: Array<{ number: number; reason: string }> = []
+
+  for (const pr of prs) {
+    console.log(`\nProcessing PR #${pr.number}: ${pr.title}`)
+
+    const fetchPR = await $`git fetch origin pull/${pr.number}/head:pr-${pr.number}`.nothrow()
+    if (fetchPR.exitCode !== 0) {
+      console.log(`  Failed to fetch PR #${pr.number}, skipping`)
+      skipped.push({ number: pr.number, reason: "Failed to fetch" })
+      continue
+    }
+
+    const merge = await $`git merge --squash pr-${pr.number}`.nothrow()
+    if (merge.exitCode !== 0) {
+      console.log(`  Squash merge failed for PR #${pr.number}`)
+      console.log(`  Error: ${merge.stderr}`)
+      await $`git reset --hard HEAD`.nothrow()
+      skipped.push({ number: pr.number, reason: `Squash merge failed: ${merge.stderr}` })
+      continue
+    }
+
+    const add = await $`git add -A`.nothrow()
+    if (add.exitCode !== 0) {
+      console.log(`  Failed to stage changes for PR #${pr.number}`)
+      await $`git reset --hard HEAD`.nothrow()
+      skipped.push({ number: pr.number, reason: "Failed to stage" })
+      continue
+    }
+
+    const status = await $`git status --porcelain`.nothrow()
+    if (status.exitCode !== 0 || !status.stdout.trim()) {
+      console.log(`  No changes to commit for PR #${pr.number}, skipping`)
+      await $`git reset --hard HEAD`.nothrow()
+      skipped.push({ number: pr.number, reason: "No changes to commit" })
+      continue
+    }
+
+    const commitMsg = `Apply PR #${pr.number}: ${pr.title}`
+    const commit = await Bun.spawn(["git", "commit", "-m", commitMsg], { stdout: "pipe", stderr: "pipe" })
+    const commitExit = await commit.exited
+    const commitStderr = await Bun.readableStreamToText(commit.stderr)
+
+    if (commitExit !== 0) {
+      console.log(`  Failed to commit PR #${pr.number}`)
+      console.log(`  Error: ${commitStderr}`)
+      await $`git reset --hard HEAD`.nothrow()
+      skipped.push({ number: pr.number, reason: `Commit failed: ${commitStderr}` })
+      continue
+    }
+
+    console.log(`  Successfully applied PR #${pr.number}`)
+    applied.push(pr.number)
+  }
+
+  console.log("\n--- Summary ---")
+  console.log(`Applied: ${applied.length} PRs`)
+  applied.forEach((num) => console.log(`  - PR #${num}`))
+  console.log(`Skipped: ${skipped.length} PRs`)
+  skipped.forEach((x) => console.log(`  - PR #${x.number}: ${x.reason}`))
+
+  console.log("\nForce pushing beta branch...")
+  const push = await $`git push origin beta --force`.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)
+})
+
+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 }
+    },
+  }
+}