From aa92ef37fd4f3b7cfd9e07283100553ff82ae5ea Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Thu, 29 Jan 2026 10:55:39 -0600 Subject: [PATCH 01/12] tweak: add 'skill' to permissions config section so that ides will show it as autocomplete option (this is already a respected setting) --- packages/opencode/src/config/config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 8c65726e23..04a3b1e9c2 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -540,6 +540,7 @@ export namespace Config { codesearch: PermissionAction.optional(), lsp: PermissionRule.optional(), doom_loop: PermissionAction.optional(), + skill: PermissionRule.optional(), }) .catchall(PermissionRule) .or(PermissionAction), From 5a56e8172f68270bbf115906779423dfd0d0187d Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 29 Jan 2026 12:29:21 -0500 Subject: [PATCH 02/12] zen: m2.1 and glm4.7 free models --- packages/web/src/content/docs/zen.mdx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/web/src/content/docs/zen.mdx b/packages/web/src/content/docs/zen.mdx index 9fc732d057..ae7a982964 100644 --- a/packages/web/src/content/docs/zen.mdx +++ b/packages/web/src/content/docs/zen.mdx @@ -82,7 +82,9 @@ You can also access our models through the following API endpoints. | Gemini 3 Pro | gemini-3-pro | `https://opencode.ai/zen/v1/models/gemini-3-pro` | `@ai-sdk/google` | | Gemini 3 Flash | gemini-3-flash | `https://opencode.ai/zen/v1/models/gemini-3-flash` | `@ai-sdk/google` | | MiniMax M2.1 | minimax-m2.1 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| MiniMax M2.1 Free | minimax-m2.1-free | `https://opencode.ai/zen/v1/messages` | `@ai-sdk/anthropic` | | GLM 4.7 | glm-4.7 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | +| GLM 4.7 Free | glm-4.7-free | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | GLM 4.6 | glm-4.6 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2.5 | kimi-k2.5 | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | | Kimi K2 Thinking | kimi-k2-thinking | `https://opencode.ai/zen/v1/chat/completions` | `@ai-sdk/openai-compatible` | @@ -113,7 +115,9 @@ We support a pay-as-you-go model. Below are the prices **per 1M tokens**. | Model | Input | Output | Cached Read | Cached Write | | --------------------------------- | ------ | ------ | ----------- | ------------ | | Big Pickle | Free | Free | Free | - | +| MiniMax M2.1 Free | Free | Free | Free | - | | MiniMax M2.1 | $0.30 | $1.20 | $0.10 | - | +| GLM 4.7 Free | Free | Free | Free | - | | GLM 4.7 | $0.60 | $2.20 | $0.10 | - | | GLM 4.6 | $0.60 | $2.20 | $0.10 | - | | Kimi K2.5 | $0.60 | $3.00 | $0.10 | - | @@ -149,6 +153,8 @@ Credit card fees are passed along at cost (4.4% + $0.30 per transaction); we don The free models: +- GLM 4.7 is currently free on OpenCode for a limited time. The team is using this time to collect feedback and improve the model. +- MiniMax M2.1 is currently free on OpenCode for a limited time. The team is using this time to collect feedback and improve the model. - Big Pickle is a stealth model that's free on OpenCode for a limited time. The team is using this time to collect feedback and improve the model. Contact us if you have any questions. @@ -178,6 +184,8 @@ charging you more than $20 if your balance goes below $5. All our models are hosted in the US. Our providers follow a zero-retention policy and do not use your data for model training, with the following exceptions: +- GLM 4.7: During its free period, collected data may be used to improve the model. +- MiniMax M2.1: During its free period, collected data may be used to improve the model. - Big Pickle: During its free period, collected data may be used to improve the model. - OpenAI APIs: Requests are retained for 30 days in accordance with [OpenAI's Data Policies](https://platform.openai.com/docs/guides/your-data). - Anthropic APIs: Requests are retained for 30 days in accordance with [Anthropic's Data Policies](https://docs.anthropic.com/en/docs/claude-code/data-usage). From 45ec3105b18c7c0700f05cdb82ea733e584c17fc Mon Sep 17 00:00:00 2001 From: Spoon <212802214+spoons-and-mirrors@users.noreply.github.com> Date: Thu, 29 Jan 2026 18:47:06 +0100 Subject: [PATCH 03/12] feat: support config skill registration (#9640) Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> --- packages/opencode/src/config/config.ts | 6 ++++++ packages/opencode/src/skill/skill.ts | 21 +++++++++++++++++++++ packages/opencode/src/tool/skill.ts | 5 ++--- packages/sdk/js/src/v2/gen/types.gen.ts | 9 +++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 04a3b1e9c2..adf733e322 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -560,6 +560,11 @@ export namespace Config { }) export type Command = z.infer + export const Skills = z.object({ + paths: z.array(z.string()).optional().describe("Additional paths to skill folders"), + }) + export type Skills = z.infer + export const Agent = z .object({ model: z.string().optional(), @@ -895,6 +900,7 @@ export namespace Config { .record(z.string(), Command) .optional() .describe("Command configuration, see https://opencode.ai/docs/commands"), + skills: Skills.optional().describe("Additional skill folder paths"), watcher: z .object({ ignore: z.array(z.string()).optional(), diff --git a/packages/opencode/src/skill/skill.ts b/packages/opencode/src/skill/skill.ts index 12fc9ee90c..5b300a9287 100644 --- a/packages/opencode/src/skill/skill.ts +++ b/packages/opencode/src/skill/skill.ts @@ -1,5 +1,6 @@ import z from "zod" import path from "path" +import os from "os" import { Config } from "../config/config" import { Instance } from "../project/instance" import { NamedError } from "@opencode-ai/util/error" @@ -40,6 +41,7 @@ export namespace Skill { const OPENCODE_SKILL_GLOB = new Bun.Glob("{skill,skills}/**/SKILL.md") const CLAUDE_SKILL_GLOB = new Bun.Glob("skills/**/SKILL.md") + const SKILL_GLOB = new Bun.Glob("**/SKILL.md") export const state = Instance.state(async () => { const skills: Record = {} @@ -122,6 +124,25 @@ export namespace Skill { } } + // Scan additional skill paths from config + const config = await Config.get() + for (const skillPath of config.skills?.paths ?? []) { + const expanded = skillPath.startsWith("~/") ? path.join(os.homedir(), skillPath.slice(2)) : skillPath + const resolved = path.isAbsolute(expanded) ? expanded : path.join(Instance.directory, expanded) + if (!(await Filesystem.isDir(resolved))) { + log.warn("skill path not found", { path: resolved }) + continue + } + for await (const match of SKILL_GLOB.scan({ + cwd: resolved, + absolute: true, + onlyFiles: true, + followSymlinks: true, + })) { + await addSkill(match) + } + } + return skills }) diff --git a/packages/opencode/src/tool/skill.ts b/packages/opencode/src/tool/skill.ts index 9536685ef9..76d9fd535e 100644 --- a/packages/opencode/src/tool/skill.ts +++ b/packages/opencode/src/tool/skill.ts @@ -62,12 +62,11 @@ export const SkillTool = Tool.define("skill", async (ctx) => { always: [params.name], metadata: {}, }) - // Load and parse skill content - const parsed = await ConfigMarkdown.parse(skill.location) + const content = (await ConfigMarkdown.parse(skill.location)).content const dir = path.dirname(skill.location) // Format output similar to plugin pattern - const output = [`## Skill: ${skill.name}`, "", `**Base directory**: ${dir}`, "", parsed.content.trim()].join("\n") + const output = [`## Skill: ${skill.name}`, "", `**Base directory**: ${dir}`, "", content.trim()].join("\n") return { title: `Loaded skill: ${skill.name}`, diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 12c7bf7dfd..ceab04c19f 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1633,6 +1633,15 @@ export type Config = { subtask?: boolean } } + /** + * Additional skill folder paths to scan + */ + skills?: { + /** + * Additional paths to skill folders to scan + */ + paths?: Array + } watcher?: { ignore?: Array } From 8dedb3f4ae5fa99444b1a71d689bafc1f5581240 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Thu, 29 Jan 2026 11:49:22 -0600 Subject: [PATCH 04/12] chore: regen sdk --- packages/sdk/js/src/v2/gen/types.gen.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index ceab04c19f..8555e84384 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -1364,6 +1364,7 @@ export type PermissionConfig = codesearch?: PermissionActionConfig lsp?: PermissionRuleConfig doom_loop?: PermissionActionConfig + skill?: PermissionRuleConfig [key: string]: PermissionRuleConfig | Array | PermissionActionConfig | undefined } | PermissionActionConfig @@ -1634,11 +1635,11 @@ export type Config = { } } /** - * Additional skill folder paths to scan + * Additional skill folder paths */ skills?: { /** - * Additional paths to skill folders to scan + * Additional paths to skill folders */ paths?: Array } From f996e05b4249caa292bbc0e2764480eb93dcb522 Mon Sep 17 00:00:00 2001 From: Aiden Cline Date: Thu, 29 Jan 2026 12:13:29 -0600 Subject: [PATCH 05/12] chore: format code --- .github/workflows/contributors-label.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/contributors-label.yml b/.github/workflows/contributors-label.yml index ac2214241c..e97c5c4704 100644 --- a/.github/workflows/contributors-label.yml +++ b/.github/workflows/contributors-label.yml @@ -13,7 +13,7 @@ jobs: permissions: pull-requests: write issues: write - + steps: - name: Add Contributor Label uses: actions/github-script@v8 From ae9199e1014ca042262f53593167577a4e66f17c Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 29 Jan 2026 18:14:15 +0000 Subject: [PATCH 06/12] chore: generate --- packages/sdk/openapi.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 0c60bdd407..dc2e51e5fb 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -8994,6 +8994,9 @@ }, "doom_loop": { "$ref": "#/components/schemas/PermissionActionConfig" + }, + "skill": { + "$ref": "#/components/schemas/PermissionRuleConfig" } }, "additionalProperties": { @@ -9506,6 +9509,19 @@ "required": ["template"] } }, + "skills": { + "description": "Additional skill folder paths", + "type": "object", + "properties": { + "paths": { + "description": "Additional paths to skill folders", + "type": "array", + "items": { + "type": "string" + } + } + } + }, "watcher": { "type": "object", "properties": { From 9ed3b0742f3861c5cbb0974329b13a228dcfd1a7 Mon Sep 17 00:00:00 2001 From: Dax Date: Thu, 29 Jan 2026 13:17:55 -0500 Subject: [PATCH 07/12] ci (#11149) Co-authored-by: opencode --- .github/workflows/publish.yml | 163 +++++++------ bun.lock | 30 +-- packages/app/package.json | 2 +- packages/console/app/package.json | 2 +- packages/console/core/package.json | 2 +- packages/console/function/package.json | 2 +- packages/console/mail/package.json | 2 +- packages/desktop/package.json | 2 +- packages/enterprise/package.json | 2 +- packages/extensions/zed/extension.toml | 12 +- packages/function/package.json | 2 +- packages/opencode/package.json | 2 +- packages/opencode/script/build.ts | 11 + .../opencode/script/publish-registries.ts | 187 --------------- packages/opencode/script/publish.ts | 219 ++++++++++++++++-- packages/plugin/package.json | 2 +- packages/script/src/index.ts | 4 + packages/sdk/js/package.json | 2 +- packages/sdk/js/script/publish.ts | 3 - packages/slack/package.json | 2 +- packages/ui/package.json | 2 +- packages/util/package.json | 2 +- packages/web/package.json | 2 +- script/publish-complete.ts | 14 -- script/{publish-start.ts => publish.ts} | 43 ++-- script/version.ts | 17 ++ sdks/vscode/package.json | 2 +- 27 files changed, 368 insertions(+), 367 deletions(-) delete mode 100644 packages/opencode/script/publish-registries.ts delete mode 100755 script/publish-complete.ts rename script/{publish-start.ts => publish.ts} (78%) create mode 100755 script/version.ts diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8d7a823b14..2966ceb66f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,6 +4,7 @@ run-name: "${{ format('release {0}', inputs.bump) }}" on: push: branches: + - ci - dev - snapshot-* workflow_dispatch: @@ -29,56 +30,46 @@ permissions: packages: write jobs: - publish: + version: runs-on: blacksmith-4vcpu-ubuntu-2404 if: github.repository == 'anomalyco/opencode' steps: - uses: actions/checkout@v3 with: - fetch-depth: 0 + fetch-depth: 1 + - uses: ./.github/actions/setup-bun + - id: version + run: | + ./script/version.ts + env: + GH_TOKEN: ${{ github.token }} + OPENCODE_BUMP: ${{ inputs.bump }} + OPENCODE_VERSION: ${{ inputs.version }} + outputs: + version: ${{ steps.version.outputs.version }} + release: ${{ steps.version.outputs.release }} + tag: ${{ steps.version.outputs.tag }} - - run: git fetch --force --tags + build-cli: + needs: version + runs-on: blacksmith-4vcpu-ubuntu-2404 + if: github.repository == 'anomalyco/opencode' + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 1 + fetch-tags: true - uses: ./.github/actions/setup-bun - - name: Install OpenCode - if: inputs.bump || inputs.version - run: bun i -g opencode-ai@1.0.169 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - uses: actions/setup-node@v4 - with: - node-version: "24" - registry-url: "https://registry.npmjs.org" - - - name: Setup Git Identity + - name: Build + id: build run: | - git config --global user.email "opencode@sst.dev" - git config --global user.name "opencode" - git remote set-url origin https://x-access-token:${{ secrets.SST_GITHUB_TOKEN }}@github.com/${{ github.repository }} - - - name: Publish - id: publish - run: ./script/publish-start.ts + ./packages/opencode/script/build.ts env: - OPENCODE_BUMP: ${{ inputs.bump }} - OPENCODE_VERSION: ${{ inputs.version }} - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - AUR_KEY: ${{ secrets.AUR_KEY }} - GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }} - NPM_CONFIG_PROVENANCE: false + OPENCODE_VERSION: ${{ needs.version.outputs.version }} + OPENCODE_RELEASE: ${{ needs.version.outputs.release }} + GH_TOKEN: ${{ github.token }} - uses: actions/upload-artifact@v4 with: @@ -86,12 +77,12 @@ jobs: path: packages/opencode/dist outputs: - release: ${{ steps.publish.outputs.release }} - tag: ${{ steps.publish.outputs.tag }} - version: ${{ steps.publish.outputs.version }} + version: ${{ needs.version.outputs.version }} - publish-tauri: - needs: publish + build-tauri: + needs: + - build-cli + - version continue-on-error: false strategy: fail-fast: false @@ -111,8 +102,8 @@ jobs: steps: - uses: actions/checkout@v3 with: - fetch-depth: 0 - ref: ${{ needs.publish.outputs.tag }} + fetch-depth: 1 + fetch-tags: true - uses: apple-actions/import-codesign-certs@v2 if: ${{ runner.os == 'macOS' }} @@ -134,8 +125,6 @@ jobs: run: | echo "${{ secrets.APPLE_API_KEY_PATH }}" > $RUNNER_TEMP/apple-api-key.p8 - - run: git fetch --force --tags - - uses: ./.github/actions/setup-bun - name: install dependencies (ubuntu only) @@ -160,10 +149,7 @@ jobs: bun ./scripts/prepare.ts env: OPENCODE_VERSION: ${{ needs.publish.outputs.version }} - NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }} GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }} - AUR_KEY: ${{ secrets.AUR_KEY }} - OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} RUST_TARGET: ${{ matrix.settings.target }} GH_TOKEN: ${{ github.token }} GITHUB_RUN_ID: ${{ github.run_id }} @@ -177,22 +163,18 @@ jobs: cargo tauri --version - name: Build and upload artifacts - uses: Wandalen/wretry.action@v3 + uses: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a timeout-minutes: 60 with: - attempt_limit: 3 - attempt_delay: 10000 - action: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a - with: | - projectPath: packages/desktop - uploadWorkflowArtifacts: true - tauriScript: ${{ (contains(matrix.settings.host, 'ubuntu') && 'cargo tauri') || '' }} - args: --target ${{ matrix.settings.target }} --config ./src-tauri/tauri.prod.conf.json --verbose - updaterJsonPreferNsis: true - releaseId: ${{ needs.publish.outputs.release }} - tagName: ${{ needs.publish.outputs.tag }} - releaseAssetNamePattern: opencode-desktop-[platform]-[arch][ext] - releaseDraft: true + projectPath: packages/desktop + uploadWorkflowArtifacts: true + tauriScript: ${{ (contains(matrix.settings.host, 'ubuntu') && 'cargo tauri') || '' }} + args: --target ${{ matrix.settings.target }} --config ./src-tauri/tauri.prod.conf.json --verbose + updaterJsonPreferNsis: true + releaseId: ${{ needs.version.outputs.release }} + tagName: ${{ needs.version.outputs.tag }} + releaseDraft: true + releaseAssetNamePattern: opencode-desktop-[platform]-[arch][ext] env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAURI_BUNDLER_NEW_APPIMAGE_FORMAT: true @@ -205,20 +187,52 @@ jobs: APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} APPLE_API_KEY_PATH: ${{ runner.temp }}/apple-api-key.p8 - publish-release: + publish: needs: - - publish - - publish-tauri - if: needs.publish.outputs.tag + - version + - build-cli + - build-tauri runs-on: blacksmith-4vcpu-ubuntu-2404 steps: - uses: actions/checkout@v3 with: - fetch-depth: 0 - ref: ${{ needs.publish.outputs.tag }} + fetch-depth: 1 + + - name: Install OpenCode + if: inputs.bump || inputs.version + run: bun i -g opencode-ai + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - uses: actions/setup-node@v4 + with: + node-version: "24" + registry-url: "https://registry.npmjs.org" + + - name: Setup Git Identity + run: | + git config --global user.email "opencode@sst.dev" + git config --global user.name "opencode" + git remote set-url origin https://x-access-token:${{ secrets.SST_GITHUB_TOKEN }}@github.com/${{ github.repository }} - uses: ./.github/actions/setup-bun + - uses: actions/download-artifact@v4 + with: + name: opencode-cli + path: packages/opencode/dist + - name: Setup SSH for AUR run: | sudo apt-get update @@ -230,8 +244,11 @@ jobs: git config --global user.name "opencode" ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts || true - - run: ./script/publish-complete.ts + - run: ./script/publish.ts env: - OPENCODE_VERSION: ${{ needs.publish.outputs.version }} + OPENCODE_VERSION: ${{ needs.version.outputs.version }} + OPENCODE_RELEASE: ${{ needs.version.outputs.release }} AUR_KEY: ${{ secrets.AUR_KEY }} GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }} + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} + NPM_CONFIG_PROVENANCE: false diff --git a/bun.lock b/bun.lock index 30b0cecb0f..bc58efe5c0 100644 --- a/bun.lock +++ b/bun.lock @@ -23,7 +23,7 @@ }, "packages/app": { "name": "@opencode-ai/app", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -73,7 +73,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -107,7 +107,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -134,7 +134,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -158,7 +158,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -182,7 +182,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "dependencies": { "@opencode-ai/app": "workspace:*", "@opencode-ai/ui": "workspace:*", @@ -213,7 +213,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -242,7 +242,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -258,7 +258,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "bin": { "opencode": "./bin/opencode", }, @@ -362,7 +362,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -382,7 +382,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "devDependencies": { "@hey-api/openapi-ts": "0.90.10", "@tsconfig/node22": "catalog:", @@ -393,7 +393,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -406,7 +406,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -448,7 +448,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "dependencies": { "zod": "catalog:", }, @@ -459,7 +459,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", diff --git a/packages/app/package.json b/packages/app/package.json index 1299c7ca14..61e7edcd16 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/app", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "description": "", "type": "module", "exports": { diff --git a/packages/console/app/package.json b/packages/console/app/package.json index f1b0aca8f5..a3d88a6baa 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-app", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/console/core/package.json b/packages/console/core/package.json index 59a9d2ecc4..90e6f90bf3 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "private": true, "type": "module", "license": "MIT", diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 8cfdc1c0af..1a64b46971 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 622d34815c..eca9a1d311 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 0047cde20a..3ff4bff46a 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/desktop", "private": true, - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index d0e2b0135d..996e7ed943 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "private": true, "type": "module", "license": "MIT", diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 9e09a12f99..4e0db0d712 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "opencode" name = "OpenCode" description = "The open source coding agent." -version = "1.1.42" +version = "0.0.0-ci-202601291718" schema_version = 1 authors = ["Anomaly"] repository = "https://github.com/anomalyco/opencode" @@ -11,26 +11,26 @@ name = "OpenCode" icon = "./icons/opencode.svg" [agent_servers.opencode.targets.darwin-aarch64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.42/opencode-darwin-arm64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v0.0.0-ci-202601291718/opencode-darwin-arm64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.darwin-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.42/opencode-darwin-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v0.0.0-ci-202601291718/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.42/opencode-linux-arm64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v0.0.0-ci-202601291718/opencode-linux-arm64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.42/opencode-linux-x64.tar.gz" +archive = "https://github.com/anomalyco/opencode/releases/download/v0.0.0-ci-202601291718/opencode-linux-x64.tar.gz" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.windows-x86_64] -archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.42/opencode-windows-x64.zip" +archive = "https://github.com/anomalyco/opencode/releases/download/v0.0.0-ci-202601291718/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index 34a0f5c7ba..a0d333a349 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/opencode/package.json b/packages/opencode/package.json index bb322556df..0818122b3e 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "name": "opencode", "type": "module", "license": "MIT", diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts index 12902b1cfc..84a278224b 100755 --- a/packages/opencode/script/build.ts +++ b/packages/opencode/script/build.ts @@ -179,4 +179,15 @@ for (const item of targets) { binaries[name] = Script.version } +if (Script.release) { + for (const key of Object.keys(binaries)) { + if (key.includes("linux")) { + await $`tar -czf ../../${key}.tar.gz *`.cwd(`dist/${key}/bin`) + } else { + await $`zip -r ../../${key}.zip *`.cwd(`dist/${key}/bin`) + } + } + await $`gh release upload v${Script.version} ./dist/*.zip ./dist/*.tar.gz --clobber` +} + export { binaries } diff --git a/packages/opencode/script/publish-registries.ts b/packages/opencode/script/publish-registries.ts deleted file mode 100644 index efcf4f4c6f..0000000000 --- a/packages/opencode/script/publish-registries.ts +++ /dev/null @@ -1,187 +0,0 @@ -#!/usr/bin/env bun -import { $ } from "bun" -import { Script } from "@opencode-ai/script" - -if (!Script.preview) { - // Calculate SHA values - const arm64Sha = await $`sha256sum ./dist/opencode-linux-arm64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim()) - const x64Sha = await $`sha256sum ./dist/opencode-linux-x64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim()) - const macX64Sha = await $`sha256sum ./dist/opencode-darwin-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) - const macArm64Sha = await $`sha256sum ./dist/opencode-darwin-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) - - const [pkgver, _subver = ""] = Script.version.split(/(-.*)/, 2) - - // arch - const binaryPkgbuild = [ - "# Maintainer: dax", - "# Maintainer: adam", - "", - "pkgname='opencode-bin'", - `pkgver=${pkgver}`, - `_subver=${_subver}`, - "options=('!debug' '!strip')", - "pkgrel=1", - "pkgdesc='The AI coding agent built for the terminal.'", - "url='https://github.com/anomalyco/opencode'", - "arch=('aarch64' 'x86_64')", - "license=('MIT')", - "provides=('opencode')", - "conflicts=('opencode')", - "depends=('ripgrep')", - "", - `source_aarch64=("\${pkgname}_\${pkgver}_aarch64.tar.gz::https://github.com/anomalyco/opencode/releases/download/v\${pkgver}\${_subver}/opencode-linux-arm64.tar.gz")`, - `sha256sums_aarch64=('${arm64Sha}')`, - - `source_x86_64=("\${pkgname}_\${pkgver}_x86_64.tar.gz::https://github.com/anomalyco/opencode/releases/download/v\${pkgver}\${_subver}/opencode-linux-x64.tar.gz")`, - `sha256sums_x86_64=('${x64Sha}')`, - "", - "package() {", - ' install -Dm755 ./opencode "${pkgdir}/usr/bin/opencode"', - "}", - "", - ].join("\n") - - // Source-based PKGBUILD for opencode - const sourcePkgbuild = [ - "# Maintainer: dax", - "# Maintainer: adam", - "", - "pkgname='opencode'", - `pkgver=${pkgver}`, - `_subver=${_subver}`, - "options=('!debug' '!strip')", - "pkgrel=1", - "pkgdesc='The AI coding agent built for the terminal.'", - "url='https://github.com/anomalyco/opencode'", - "arch=('aarch64' 'x86_64')", - "license=('MIT')", - "provides=('opencode')", - "conflicts=('opencode-bin')", - "depends=('ripgrep')", - "makedepends=('git' 'bun' 'go')", - "", - `source=("opencode-\${pkgver}.tar.gz::https://github.com/anomalyco/opencode/archive/v\${pkgver}\${_subver}.tar.gz")`, - `sha256sums=('SKIP')`, - "", - "build() {", - ` cd "opencode-\${pkgver}"`, - ` bun install`, - " cd ./packages/opencode", - ` OPENCODE_CHANNEL=latest OPENCODE_VERSION=${pkgver} bun run ./script/build.ts --single`, - "}", - "", - "package() {", - ` cd "opencode-\${pkgver}/packages/opencode"`, - ' mkdir -p "${pkgdir}/usr/bin"', - ' target_arch="x64"', - ' case "$CARCH" in', - ' x86_64) target_arch="x64" ;;', - ' aarch64) target_arch="arm64" ;;', - ' *) printf "unsupported architecture: %s\\n" "$CARCH" >&2 ; return 1 ;;', - " esac", - ' libc=""', - " if command -v ldd >/dev/null 2>&1; then", - " if ldd --version 2>&1 | grep -qi musl; then", - ' libc="-musl"', - " fi", - " fi", - ' if [ -z "$libc" ] && ls /lib/ld-musl-* >/dev/null 2>&1; then', - ' libc="-musl"', - " fi", - ' base=""', - ' if [ "$target_arch" = "x64" ]; then', - " if ! grep -qi avx2 /proc/cpuinfo 2>/dev/null; then", - ' base="-baseline"', - " fi", - " fi", - ' bin="dist/opencode-linux-${target_arch}${base}${libc}/bin/opencode"', - ' if [ ! -f "$bin" ]; then', - ' printf "unable to find binary for %s%s%s\\n" "$target_arch" "$base" "$libc" >&2', - " return 1", - " fi", - ' install -Dm755 "$bin" "${pkgdir}/usr/bin/opencode"', - "}", - "", - ].join("\n") - - for (const [pkg, pkgbuild] of [ - ["opencode-bin", binaryPkgbuild], - ["opencode", sourcePkgbuild], - ]) { - for (let i = 0; i < 30; i++) { - try { - await $`rm -rf ./dist/aur-${pkg}` - await $`git clone ssh://aur@aur.archlinux.org/${pkg}.git ./dist/aur-${pkg}` - await $`cd ./dist/aur-${pkg} && git checkout master` - await Bun.file(`./dist/aur-${pkg}/PKGBUILD`).write(pkgbuild) - await $`cd ./dist/aur-${pkg} && makepkg --printsrcinfo > .SRCINFO` - await $`cd ./dist/aur-${pkg} && git add PKGBUILD .SRCINFO` - await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${Script.version}"` - await $`cd ./dist/aur-${pkg} && git push` - break - } catch (e) { - continue - } - } - } - - // Homebrew formula - const homebrewFormula = [ - "# typed: false", - "# frozen_string_literal: true", - "", - "# This file was generated by GoReleaser. DO NOT EDIT.", - "class Opencode < Formula", - ` desc "The AI coding agent built for the terminal."`, - ` homepage "https://github.com/anomalyco/opencode"`, - ` version "${Script.version.split("-")[0]}"`, - "", - ` depends_on "ripgrep"`, - "", - " on_macos do", - " if Hardware::CPU.intel?", - ` url "https://github.com/anomalyco/opencode/releases/download/v${Script.version}/opencode-darwin-x64.zip"`, - ` sha256 "${macX64Sha}"`, - "", - " def install", - ' bin.install "opencode"', - " end", - " end", - " if Hardware::CPU.arm?", - ` url "https://github.com/anomalyco/opencode/releases/download/v${Script.version}/opencode-darwin-arm64.zip"`, - ` sha256 "${macArm64Sha}"`, - "", - " def install", - ' bin.install "opencode"', - " end", - " end", - " end", - "", - " on_linux do", - " if Hardware::CPU.intel? and Hardware::CPU.is_64_bit?", - ` url "https://github.com/anomalyco/opencode/releases/download/v${Script.version}/opencode-linux-x64.tar.gz"`, - ` sha256 "${x64Sha}"`, - " def install", - ' bin.install "opencode"', - " end", - " end", - " if Hardware::CPU.arm? and Hardware::CPU.is_64_bit?", - ` url "https://github.com/anomalyco/opencode/releases/download/v${Script.version}/opencode-linux-arm64.tar.gz"`, - ` sha256 "${arm64Sha}"`, - " def install", - ' bin.install "opencode"', - " end", - " end", - " end", - "end", - "", - "", - ].join("\n") - - await $`rm -rf ./dist/homebrew-tap` - await $`git clone https://${process.env["GITHUB_TOKEN"]}@github.com/sst/homebrew-tap.git ./dist/homebrew-tap` - await Bun.file("./dist/homebrew-tap/opencode.rb").write(homebrewFormula) - await $`cd ./dist/homebrew-tap && git add opencode.rb` - await $`cd ./dist/homebrew-tap && git commit -m "Update to v${Script.version}"` - await $`cd ./dist/homebrew-tap && git push` -} diff --git a/packages/opencode/script/publish.ts b/packages/opencode/script/publish.ts index 4e5846d27e..8cdeb35b40 100755 --- a/packages/opencode/script/publish.ts +++ b/packages/opencode/script/publish.ts @@ -7,12 +7,13 @@ import { fileURLToPath } from "url" const dir = fileURLToPath(new URL("..", import.meta.url)) process.chdir(dir) -const { binaries } = await import("./build.ts") -{ - const name = `${pkg.name}-${process.platform}-${process.arch}` - console.log(`smoke test: running dist/${name}/bin/opencode --version`) - await $`./dist/${name}/bin/opencode --version` +const binaries: Record = {} +for (const filepath of new Bun.Glob("*/package.json").scanSync({ cwd: "./dist" })) { + const pkg = await Bun.file(`./dist/${filepath}`).json() + binaries[pkg.name] = pkg.version } +console.log("binaries", binaries) +const version = Object.values(binaries)[0] await $`mkdir -p ./dist/${pkg.name}` await $`cp -r ./bin ./dist/${pkg.name}/bin` @@ -28,7 +29,7 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write( scripts: { postinstall: "bun ./postinstall.mjs || node ./postinstall.mjs", }, - version: Script.version, + version: version, optionalDependencies: binaries, }, null, @@ -36,35 +37,203 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write( ), ) -const tags = [Script.channel] - const tasks = Object.entries(binaries).map(async ([name]) => { if (process.platform !== "win32") { await $`chmod -R 755 .`.cwd(`./dist/${name}`) } await $`bun pm pack`.cwd(`./dist/${name}`) - for (const tag of tags) { - await $`npm publish *.tgz --access public --tag ${tag}`.cwd(`./dist/${name}`) - } + await $`npm publish *.tgz --access public --tag ${Script.channel}`.cwd(`./dist/${name}`) }) await Promise.all(tasks) -for (const tag of tags) { - await $`cd ./dist/${pkg.name} && bun pm pack && npm publish *.tgz --access public --tag ${tag}` -} +await $`cd ./dist/${pkg.name} && bun pm pack && npm publish *.tgz --access public --tag ${Script.channel}` +const image = "ghcr.io/anomalyco/opencode" +const platforms = "linux/amd64,linux/arm64" +const tags = [`${image}:${version}`, `${image}:${Script.channel}`] +const tagFlags = tags.flatMap((t) => ["-t", t]) +await $`docker buildx build --platform ${platforms} ${tagFlags} --push .` + +// registries if (!Script.preview) { - // Create archives for GitHub release - for (const key of Object.keys(binaries)) { - if (key.includes("linux")) { - await $`tar -czf ../../${key}.tar.gz *`.cwd(`dist/${key}/bin`) - } else { - await $`zip -r ../../${key}.zip *`.cwd(`dist/${key}/bin`) + // Calculate SHA values + const arm64Sha = await $`sha256sum ./dist/opencode-linux-arm64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim()) + const x64Sha = await $`sha256sum ./dist/opencode-linux-x64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim()) + const macX64Sha = await $`sha256sum ./dist/opencode-darwin-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) + const macArm64Sha = await $`sha256sum ./dist/opencode-darwin-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) + + const [pkgver, _subver = ""] = Script.version.split(/(-.*)/, 2) + + // arch + const binaryPkgbuild = [ + "# Maintainer: dax", + "# Maintainer: adam", + "", + "pkgname='opencode-bin'", + `pkgver=${pkgver}`, + `_subver=${_subver}`, + "options=('!debug' '!strip')", + "pkgrel=1", + "pkgdesc='The AI coding agent built for the terminal.'", + "url='https://github.com/anomalyco/opencode'", + "arch=('aarch64' 'x86_64')", + "license=('MIT')", + "provides=('opencode')", + "conflicts=('opencode')", + "depends=('ripgrep')", + "", + `source_aarch64=("\${pkgname}_\${pkgver}_aarch64.tar.gz::https://github.com/anomalyco/opencode/releases/download/v\${pkgver}\${_subver}/opencode-linux-arm64.tar.gz")`, + `sha256sums_aarch64=('${arm64Sha}')`, + + `source_x86_64=("\${pkgname}_\${pkgver}_x86_64.tar.gz::https://github.com/anomalyco/opencode/releases/download/v\${pkgver}\${_subver}/opencode-linux-x64.tar.gz")`, + `sha256sums_x86_64=('${x64Sha}')`, + "", + "package() {", + ' install -Dm755 ./opencode "${pkgdir}/usr/bin/opencode"', + "}", + "", + ].join("\n") + + // Source-based PKGBUILD for opencode + const sourcePkgbuild = [ + "# Maintainer: dax", + "# Maintainer: adam", + "", + "pkgname='opencode'", + `pkgver=${pkgver}`, + `_subver=${_subver}`, + "options=('!debug' '!strip')", + "pkgrel=1", + "pkgdesc='The AI coding agent built for the terminal.'", + "url='https://github.com/anomalyco/opencode'", + "arch=('aarch64' 'x86_64')", + "license=('MIT')", + "provides=('opencode')", + "conflicts=('opencode-bin')", + "depends=('ripgrep')", + "makedepends=('git' 'bun' 'go')", + "", + `source=("opencode-\${pkgver}.tar.gz::https://github.com/anomalyco/opencode/archive/v\${pkgver}\${_subver}.tar.gz")`, + `sha256sums=('SKIP')`, + "", + "build() {", + ` cd "opencode-\${pkgver}"`, + ` bun install`, + " cd ./packages/opencode", + ` OPENCODE_CHANNEL=latest OPENCODE_VERSION=${pkgver} bun run ./script/build.ts --single`, + "}", + "", + "package() {", + ` cd "opencode-\${pkgver}/packages/opencode"`, + ' mkdir -p "${pkgdir}/usr/bin"', + ' target_arch="x64"', + ' case "$CARCH" in', + ' x86_64) target_arch="x64" ;;', + ' aarch64) target_arch="arm64" ;;', + ' *) printf "unsupported architecture: %s\\n" "$CARCH" >&2 ; return 1 ;;', + " esac", + ' libc=""', + " if command -v ldd >/dev/null 2>&1; then", + " if ldd --version 2>&1 | grep -qi musl; then", + ' libc="-musl"', + " fi", + " fi", + ' if [ -z "$libc" ] && ls /lib/ld-musl-* >/dev/null 2>&1; then', + ' libc="-musl"', + " fi", + ' base=""', + ' if [ "$target_arch" = "x64" ]; then', + " if ! grep -qi avx2 /proc/cpuinfo 2>/dev/null; then", + ' base="-baseline"', + " fi", + " fi", + ' bin="dist/opencode-linux-${target_arch}${base}${libc}/bin/opencode"', + ' if [ ! -f "$bin" ]; then', + ' printf "unable to find binary for %s%s%s\\n" "$target_arch" "$base" "$libc" >&2', + " return 1", + " fi", + ' install -Dm755 "$bin" "${pkgdir}/usr/bin/opencode"', + "}", + "", + ].join("\n") + + for (const [pkg, pkgbuild] of [ + ["opencode-bin", binaryPkgbuild], + ["opencode", sourcePkgbuild], + ]) { + for (let i = 0; i < 30; i++) { + try { + await $`rm -rf ./dist/aur-${pkg}` + await $`git clone ssh://aur@aur.archlinux.org/${pkg}.git ./dist/aur-${pkg}` + await $`cd ./dist/aur-${pkg} && git checkout master` + await Bun.file(`./dist/aur-${pkg}/PKGBUILD`).write(pkgbuild) + await $`cd ./dist/aur-${pkg} && makepkg --printsrcinfo > .SRCINFO` + await $`cd ./dist/aur-${pkg} && git add PKGBUILD .SRCINFO` + await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${Script.version}"` + await $`cd ./dist/aur-${pkg} && git push` + break + } catch (e) { + continue + } } } - const image = "ghcr.io/anomalyco/opencode" - const platforms = "linux/amd64,linux/arm64" - const tags = [`${image}:${Script.version}`, `${image}:latest`] - const tagFlags = tags.flatMap((t) => ["-t", t]) - await $`docker buildx build --platform ${platforms} ${tagFlags} --push .` + // Homebrew formula + const homebrewFormula = [ + "# typed: false", + "# frozen_string_literal: true", + "", + "# This file was generated by GoReleaser. DO NOT EDIT.", + "class Opencode < Formula", + ` desc "The AI coding agent built for the terminal."`, + ` homepage "https://github.com/anomalyco/opencode"`, + ` version "${Script.version.split("-")[0]}"`, + "", + ` depends_on "ripgrep"`, + "", + " on_macos do", + " if Hardware::CPU.intel?", + ` url "https://github.com/anomalyco/opencode/releases/download/v${Script.version}/opencode-darwin-x64.zip"`, + ` sha256 "${macX64Sha}"`, + "", + " def install", + ' bin.install "opencode"', + " end", + " end", + " if Hardware::CPU.arm?", + ` url "https://github.com/anomalyco/opencode/releases/download/v${Script.version}/opencode-darwin-arm64.zip"`, + ` sha256 "${macArm64Sha}"`, + "", + " def install", + ' bin.install "opencode"', + " end", + " end", + " end", + "", + " on_linux do", + " if Hardware::CPU.intel? and Hardware::CPU.is_64_bit?", + ` url "https://github.com/anomalyco/opencode/releases/download/v${Script.version}/opencode-linux-x64.tar.gz"`, + ` sha256 "${x64Sha}"`, + " def install", + ' bin.install "opencode"', + " end", + " end", + " if Hardware::CPU.arm? and Hardware::CPU.is_64_bit?", + ` url "https://github.com/anomalyco/opencode/releases/download/v${Script.version}/opencode-linux-arm64.tar.gz"`, + ` sha256 "${arm64Sha}"`, + " def install", + ' bin.install "opencode"', + " end", + " end", + " end", + "end", + "", + "", + ].join("\n") + + await $`rm -rf ./dist/homebrew-tap` + await $`git clone https://${process.env["GITHUB_TOKEN"]}@github.com/sst/homebrew-tap.git ./dist/homebrew-tap` + await Bun.file("./dist/homebrew-tap/opencode.rb").write(homebrewFormula) + await $`cd ./dist/homebrew-tap && git add opencode.rb` + await $`cd ./dist/homebrew-tap && git commit -m "Update to v${Script.version}"` + await $`cd ./dist/homebrew-tap && git push` } diff --git a/packages/plugin/package.json b/packages/plugin/package.json index 6ffef240f0..36816fe1c9 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/script/src/index.ts b/packages/script/src/index.ts index e722ba5094..2d991ff0c3 100644 --- a/packages/script/src/index.ts +++ b/packages/script/src/index.ts @@ -20,6 +20,7 @@ const env = { OPENCODE_CHANNEL: process.env["OPENCODE_CHANNEL"], OPENCODE_BUMP: process.env["OPENCODE_BUMP"], OPENCODE_VERSION: process.env["OPENCODE_VERSION"], + OPENCODE_RELEASE: process.env["OPENCODE_RELEASE"], } const CHANNEL = await (async () => { if (env.OPENCODE_CHANNEL) return env.OPENCODE_CHANNEL @@ -55,5 +56,8 @@ export const Script = { get preview() { return IS_PREVIEW }, + get release() { + return env.OPENCODE_RELEASE + }, } console.log(`opencode script`, JSON.stringify(Script, null, 2)) diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 85022e30ac..ccd0b6a945 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/sdk/js/script/publish.ts b/packages/sdk/js/script/publish.ts index 79701b50e7..46dd42b700 100755 --- a/packages/sdk/js/script/publish.ts +++ b/packages/sdk/js/script/publish.ts @@ -6,13 +6,10 @@ import { $ } from "bun" const dir = new URL("..", import.meta.url).pathname process.chdir(dir) -await import("./build") - const pkg = await import("../package.json").then((m) => m.default) const original = JSON.parse(JSON.stringify(pkg)) for (const [key, value] of Object.entries(pkg.exports)) { const file = value.replace("./src/", "./dist/").replace(".ts", "") - /// @ts-expect-error pkg.exports[key] = { import: file + ".js", types: file + ".d.ts", diff --git a/packages/slack/package.json b/packages/slack/package.json index 49504a7256..dff46642af 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "type": "module", "license": "MIT", "scripts": { diff --git a/packages/ui/package.json b/packages/ui/package.json index a95f8b52ae..be261a318a 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "type": "module", "license": "MIT", "exports": { diff --git a/packages/util/package.json b/packages/util/package.json index 06677a1132..40a6080cbf 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "private": true, "type": "module", "license": "MIT", diff --git a/packages/web/package.json b/packages/web/package.json index 23da5f8e4a..666654a2ef 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -2,7 +2,7 @@ "name": "@opencode-ai/web", "type": "module", "license": "MIT", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", diff --git a/script/publish-complete.ts b/script/publish-complete.ts deleted file mode 100755 index a3bdceae07..0000000000 --- a/script/publish-complete.ts +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bun - -import { Script } from "@opencode-ai/script" -import { $ } from "bun" - -if (!Script.preview) { - await $`gh release edit v${Script.version} --draft=false` -} - -await $`bun install` - -await $`gh release download --pattern "opencode-linux-*64.tar.gz" --pattern "opencode-darwin-*64.zip" -D dist` - -await import(`../packages/opencode/script/publish-registries.ts`) diff --git a/script/publish-start.ts b/script/publish.ts similarity index 78% rename from script/publish-start.ts rename to script/publish.ts index 474e01a64a..23a6e5c9df 100755 --- a/script/publish-start.ts +++ b/script/publish.ts @@ -32,16 +32,8 @@ Add highlights before publishing. Delete this section if no highlights. ` -let notes: string[] = [] - console.log("=== publishing ===\n") -if (!Script.preview) { - const previous = await getLatestRelease() - notes = await buildNotes(previous, "HEAD") - // notes.unshift(highlightsTemplate) -} - const pkgjsons = await Array.fromAsync( new Bun.Glob("**/package.json").scan({ absolute: true, @@ -63,8 +55,22 @@ console.log("updated:", extensionToml) await Bun.file(extensionToml).write(toml) await $`bun install` +await import(`../packages/sdk/js/script/build.ts`) -console.log("\n=== opencode ===\n") +if (Script.release) { + const previous = await getLatestRelease() + const notes = await buildNotes(previous, "HEAD") + // notes.unshift(highlightsTemplate) + await $`git commit -am "release: v${Script.version}"` + await $`git tag v${Script.version}` + await $`git fetch origin` + await $`git cherry-pick HEAD..origin/dev`.nothrow() + await $`git push origin HEAD --tags --no-verify --force-with-lease` + await new Promise((resolve) => setTimeout(resolve, 5_000)) + await $`gh release edit v${Script.version} --draft=false --title "v${Script.version}" --notes ${notes.join("\n") || "No notable changes"}` +} + +console.log("\n=== cli ===\n") await import(`../packages/opencode/script/publish.ts`) console.log("\n=== sdk ===\n") @@ -75,22 +81,3 @@ await import(`../packages/plugin/script/publish.ts`) const dir = new URL("..", import.meta.url).pathname process.chdir(dir) - -let output = `version=${Script.version}\n` - -if (!Script.preview) { - await $`git commit -am "release: v${Script.version}"` - await $`git tag v${Script.version}` - await $`git fetch origin` - await $`git cherry-pick HEAD..origin/dev`.nothrow() - await $`git push origin HEAD --tags --no-verify --force-with-lease` - await new Promise((resolve) => setTimeout(resolve, 5_000)) - await $`gh release create v${Script.version} -d --title "v${Script.version}" --notes ${notes.join("\n") || "No notable changes"} ./packages/opencode/dist/*.zip ./packages/opencode/dist/*.tar.gz` - const release = await $`gh release view v${Script.version} --json id,tagName`.json() - output += `release=${release.id}\n` - output += `tag=${release.tagName}\n` -} - -if (process.env.GITHUB_OUTPUT) { - await Bun.write(process.env.GITHUB_OUTPUT, output) -} diff --git a/script/version.ts b/script/version.ts new file mode 100755 index 0000000000..861417f759 --- /dev/null +++ b/script/version.ts @@ -0,0 +1,17 @@ +#!/usr/bin/env bun + +import { Script } from "@opencode-ai/script" +import { $ } from "bun" + +let output = [`version=${Script.version}`] + +if (!Script.preview) { + await $`gh release create v${Script.version} -d --title "v${Script.version}" ${Script.preview ? "--prerelease" : ""}` + const release = await $`gh release view v${Script.version} --json id,tagName`.json() + output.push(`release=${release.id}`) + output.push(`tag=${release.tagName}`) +} + +if (process.env.GITHUB_OUTPUT) { + await Bun.write(process.env.GITHUB_OUTPUT, output.join("\n")) +} diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index 77f4ec7a4d..3de79e99ed 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.1.42", + "version": "0.0.0-ci-202601291718", "publisher": "sst-dev", "repository": { "type": "git", From a9a7595234e19f7fa34c3317919c7454bd25a73a Mon Sep 17 00:00:00 2001 From: Dax Date: Thu, 29 Jan 2026 13:57:59 -0500 Subject: [PATCH 08/12] test: skip failing tests (#11184) --- .github/workflows/close-stale-prs.yml | 2 +- .github/workflows/contributors-label.yml | 33 ------------------- .github/workflows/daily-issues-recap.yml | 2 +- .github/workflows/daily-pr-recap.yml | 2 +- .github/workflows/docs-update.yml | 2 +- .github/workflows/duplicate-issues.yml | 2 +- .github/workflows/nix-desktop.yml | 4 +-- .../{update-nix-hashes.yml => nix-hashes.yml} | 8 ++--- .github/workflows/notify-discord.yml | 2 +- .../{duplicate-prs.yml => pr-management.yml} | 25 +++++++++++++- .github/workflows/pr-standards.yml | 2 +- .github/workflows/review.yml | 2 +- .github/workflows/stale-issues.yml | 2 +- .github/workflows/triage.yml | 2 +- packages/app/e2e/file-tree.spec.ts | 2 +- packages/app/src/addons/serialize.test.ts | 2 +- .../app/src/context/layout-scroll.test.ts | 2 +- 17 files changed, 43 insertions(+), 53 deletions(-) delete mode 100644 .github/workflows/contributors-label.yml rename .github/workflows/{update-nix-hashes.yml => nix-hashes.yml} (96%) rename .github/workflows/{duplicate-prs.yml => pr-management.yml} (71%) 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/nix-desktop.yml b/.github/workflows/nix-desktop.yml index 3d7c480313..031eff6a69 100644 --- a/.github/workflows/nix-desktop.yml +++ b/.github/workflows/nix-desktop.yml @@ -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/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/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[] = [] From cf5cf7b23e55cf05741ccfd6e2cc71b3a0a7d108 Mon Sep 17 00:00:00 2001 From: Dax Date: Thu, 29 Jan 2026 14:04:12 -0500 Subject: [PATCH 09/12] chore: consolidate and standardize workflow files (#11183) From a92b7923c2baff2d0e6fd59e5ad4cd22790553ae Mon Sep 17 00:00:00 2001 From: Dax Date: Thu, 29 Jan 2026 14:16:26 -0500 Subject: [PATCH 10/12] ci: disable nix-desktop workflow (#11188) --- .../{nix-desktop.yml => nix-desktop.yml.disabled} | 9 --------- .github/workflows/test.yml | 4 ---- 2 files changed, 13 deletions(-) rename .github/workflows/{nix-desktop.yml => nix-desktop.yml.disabled} (80%) diff --git a/.github/workflows/nix-desktop.yml b/.github/workflows/nix-desktop.yml.disabled similarity index 80% rename from .github/workflows/nix-desktop.yml rename to .github/workflows/nix-desktop.yml.disabled index 031eff6a69..e14dce96bd 100644 --- a/.github/workflows/nix-desktop.yml +++ b/.github/workflows/nix-desktop.yml.disabled @@ -1,15 +1,6 @@ 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" 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 From 33311e99500fa78aeb22be0dee19d0bf9c0296c6 Mon Sep 17 00:00:00 2001 From: Dax Date: Thu, 29 Jan 2026 14:25:05 -0500 Subject: [PATCH 11/12] ci: remove push triggers from workflow files (#11186) --- .github/workflows/generate.yml | 1 + 1 file changed, 1 insertion(+) 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: From cd4075faf6b9cc80e4f9bdf2b8d4da359b0d8718 Mon Sep 17 00:00:00 2001 From: Dax Date: Thu, 29 Jan 2026 15:02:36 -0500 Subject: [PATCH 12/12] feat: add beta branch sync workflow for contributor PRs (#11190) --- .github/workflows/beta.yml | 34 ++++++ .github/workflows/nix-desktop.yml.disabled | 9 ++ .github/workflows/publish.yml | 1 + script/beta.ts | 127 +++++++++++++++++++++ 4 files changed, 171 insertions(+) create mode 100644 .github/workflows/beta.yml create mode 100755 script/beta.ts 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/nix-desktop.yml.disabled b/.github/workflows/nix-desktop.yml.disabled index e14dce96bd..031eff6a69 100644 --- a/.github/workflows/nix-desktop.yml.disabled +++ b/.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" 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/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 } + }, + } +}