diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml new file mode 100644 index 0000000000..d92133ea6a --- /dev/null +++ b/.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 diff --git a/.github/workflows/close-stale-prs.yml b/.github/workflows/close-stale-prs.yml index 787ee02e62..cb5c45063f 100644 --- a/.github/workflows/close-stale-prs.yml +++ b/.github/workflows/close-stale-prs.yml @@ -1,4 +1,4 @@ -name: Close stale PRs +name: close-stale-prs on: workflow_dispatch: diff --git a/.github/workflows/contributors-label.yml b/.github/workflows/contributors-label.yml deleted file mode 100644 index e97c5c4704..0000000000 --- a/.github/workflows/contributors-label.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Add Contributors Label - -on: - # issues: - # types: [opened] - - pull_request_target: - types: [opened] - -jobs: - add-contributor-label: - runs-on: ubuntu-latest - permissions: - pull-requests: write - issues: write - - steps: - - name: Add Contributor Label - uses: actions/github-script@v8 - with: - script: | - const isPR = !!context.payload.pull_request; - const issueNumber = isPR ? context.payload.pull_request.number : context.payload.issue.number; - const authorAssociation = isPR ? context.payload.pull_request.author_association : context.payload.issue.author_association; - - if (authorAssociation === 'CONTRIBUTOR') { - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issueNumber, - labels: ['contributor'] - }); - } diff --git a/.github/workflows/daily-issues-recap.yml b/.github/workflows/daily-issues-recap.yml index a333e5365f..79543fcb19 100644 --- a/.github/workflows/daily-issues-recap.yml +++ b/.github/workflows/daily-issues-recap.yml @@ -1,4 +1,4 @@ -name: Daily Issues Recap +name: daily-issues-recap on: schedule: diff --git a/.github/workflows/daily-pr-recap.yml b/.github/workflows/daily-pr-recap.yml index 7c8bab395f..7ca94bd237 100644 --- a/.github/workflows/daily-pr-recap.yml +++ b/.github/workflows/daily-pr-recap.yml @@ -1,4 +1,4 @@ -name: Daily PR Recap +name: daily-pr-recap on: schedule: diff --git a/.github/workflows/docs-update.yml b/.github/workflows/docs-update.yml index a8dd2ae4f2..900ad2b0c5 100644 --- a/.github/workflows/docs-update.yml +++ b/.github/workflows/docs-update.yml @@ -1,4 +1,4 @@ -name: Docs Update +name: docs-update on: schedule: diff --git a/.github/workflows/duplicate-issues.yml b/.github/workflows/duplicate-issues.yml index 53aa2a725e..cbe8df5175 100644 --- a/.github/workflows/duplicate-issues.yml +++ b/.github/workflows/duplicate-issues.yml @@ -1,4 +1,4 @@ -name: Duplicate Issue Detection +name: duplicate-issues on: issues: diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index 29cc989539..cbbab479e1 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -4,6 +4,7 @@ on: push: branches: - dev + pull_request: workflow_dispatch: jobs: diff --git a/.github/workflows/nix-desktop.yml b/.github/workflows/nix-desktop.yml.disabled similarity index 96% rename from .github/workflows/nix-desktop.yml rename to .github/workflows/nix-desktop.yml.disabled index 3d7c480313..031eff6a69 100644 --- a/.github/workflows/nix-desktop.yml +++ b/.github/workflows/nix-desktop.yml.disabled @@ -1,4 +1,4 @@ -name: nix desktop +name: nix-desktop on: push: @@ -21,7 +21,7 @@ on: workflow_dispatch: jobs: - build-desktop: + nix-desktop: strategy: fail-fast: false matrix: diff --git a/.github/workflows/update-nix-hashes.yml b/.github/workflows/nix-hashes.yml similarity index 96% rename from .github/workflows/update-nix-hashes.yml rename to .github/workflows/nix-hashes.yml index 7175f4fbdd..63ab561887 100644 --- a/.github/workflows/update-nix-hashes.yml +++ b/.github/workflows/nix-hashes.yml @@ -1,4 +1,4 @@ -name: Update Nix Hashes +name: nix-hashes permissions: contents: write @@ -11,17 +11,17 @@ on: - "package.json" - "packages/*/package.json" - "flake.lock" - - ".github/workflows/update-nix-hashes.yml" + - ".github/workflows/nix-hashes.yml" pull_request: paths: - "bun.lock" - "package.json" - "packages/*/package.json" - "flake.lock" - - ".github/workflows/update-nix-hashes.yml" + - ".github/workflows/nix-hashes.yml" jobs: - update-node-modules-hashes: + nix-hashes: if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository runs-on: blacksmith-4vcpu-ubuntu-2404 env: diff --git a/.github/workflows/notify-discord.yml b/.github/workflows/notify-discord.yml index 62577ecf00..b1d8053603 100644 --- a/.github/workflows/notify-discord.yml +++ b/.github/workflows/notify-discord.yml @@ -1,4 +1,4 @@ -name: discord +name: notify-discord on: release: diff --git a/.github/workflows/duplicate-prs.yml b/.github/workflows/pr-management.yml similarity index 71% rename from .github/workflows/duplicate-prs.yml rename to .github/workflows/pr-management.yml index 3260685895..25bea2f24f 100644 --- a/.github/workflows/duplicate-prs.yml +++ b/.github/workflows/pr-management.yml @@ -1,4 +1,4 @@ -name: Duplicate PR Check +name: pr-management on: pull_request_target: @@ -63,3 +63,26 @@ jobs: gh pr comment "$PR_NUMBER" --body "_The following comment was made by an LLM, it may be inaccurate:_ $COMMENT" + + add-contributor-label: + runs-on: ubuntu-latest + permissions: + pull-requests: write + issues: write + steps: + - name: Add Contributor Label + uses: actions/github-script@v8 + with: + script: | + const isPR = !!context.payload.pull_request; + const issueNumber = isPR ? context.payload.pull_request.number : context.payload.issue.number; + const authorAssociation = isPR ? context.payload.pull_request.author_association : context.payload.issue.author_association; + + if (authorAssociation === 'CONTRIBUTOR') { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + labels: ['contributor'] + }); + } diff --git a/.github/workflows/pr-standards.yml b/.github/workflows/pr-standards.yml index c1cf175678..397f794a1c 100644 --- a/.github/workflows/pr-standards.yml +++ b/.github/workflows/pr-standards.yml @@ -1,4 +1,4 @@ -name: PR Standards +name: pr-standards on: pull_request_target: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2966ceb66f..caa9aca310 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -6,6 +6,7 @@ on: branches: - ci - dev + - beta - snapshot-* workflow_dispatch: inputs: diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml index 93b01bafa2..58e73fac8f 100644 --- a/.github/workflows/review.yml +++ b/.github/workflows/review.yml @@ -1,4 +1,4 @@ -name: Guidelines Check +name: review on: issue_comment: diff --git a/.github/workflows/stale-issues.yml b/.github/workflows/stale-issues.yml index b5378d7d52..a4b8583f92 100644 --- a/.github/workflows/stale-issues.yml +++ b/.github/workflows/stale-issues.yml @@ -1,4 +1,4 @@ -name: "Auto-close stale issues" +name: stale-issues on: schedule: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d95de94d23..f6cbb16edc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,9 +1,6 @@ name: test on: - push: - branches: - - dev pull_request: workflow_dispatch: jobs: @@ -20,7 +17,6 @@ jobs: command: | git config --global user.email "bot@opencode.ai" git config --global user.name "opencode" - bun turbo typecheck bun turbo test - name: windows host: windows-latest diff --git a/.github/workflows/triage.yml b/.github/workflows/triage.yml index 6e15095729..99e7b5b34f 100644 --- a/.github/workflows/triage.yml +++ b/.github/workflows/triage.yml @@ -1,4 +1,4 @@ -name: Issue Triage +name: triage on: issues: diff --git a/packages/app/e2e/file-tree.spec.ts b/packages/app/e2e/file-tree.spec.ts index c22a810f4f..0b04eb2468 100644 --- a/packages/app/e2e/file-tree.spec.ts +++ b/packages/app/e2e/file-tree.spec.ts @@ -1,6 +1,6 @@ import { test, expect } from "./fixtures" -test("file tree can expand folders and open a file", async ({ page, gotoSession }) => { +test.skip("file tree can expand folders and open a file", async ({ page, gotoSession }) => { await gotoSession() const toggle = page.getByRole("button", { name: "Toggle file tree" }) diff --git a/packages/app/src/addons/serialize.test.ts b/packages/app/src/addons/serialize.test.ts index 7f6780557d..7fb1a61f35 100644 --- a/packages/app/src/addons/serialize.test.ts +++ b/packages/app/src/addons/serialize.test.ts @@ -36,7 +36,7 @@ function writeAndWait(term: Terminal, data: string): Promise { }) } -describe("SerializeAddon", () => { +describe.skip("SerializeAddon", () => { describe("ANSI color preservation", () => { test("should preserve text attributes (bold, italic, underline)", async () => { const { term, addon } = createTerminal() diff --git a/packages/app/src/context/layout-scroll.test.ts b/packages/app/src/context/layout-scroll.test.ts index b7962c411c..c565653850 100644 --- a/packages/app/src/context/layout-scroll.test.ts +++ b/packages/app/src/context/layout-scroll.test.ts @@ -5,7 +5,7 @@ import { makePersisted, type SyncStorage } from "@solid-primitives/storage" import { createScrollPersistence } from "./layout-scroll" describe("createScrollPersistence", () => { - test("debounces persisted scroll writes", async () => { + test.skip("debounces persisted scroll writes", async () => { const key = "layout-scroll.test" const data = new Map() const writes: string[] = [] diff --git a/script/beta.ts b/script/beta.ts new file mode 100755 index 0000000000..569a906c8f --- /dev/null +++ b/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 } + }, + } +}