From 5f282c268db3515c87d48d475d3a5fb612e97216 Mon Sep 17 00:00:00 2001 From: adamelmore <2363879+adamdottv@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:38:18 -0600 Subject: [PATCH 01/33] fix(app): free model layout --- .../app/src/components/dialog-select-model-unpaid.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/app/src/components/dialog-select-model-unpaid.tsx b/packages/app/src/components/dialog-select-model-unpaid.tsx index 5a4dd00ec64..78c169777e0 100644 --- a/packages/app/src/components/dialog-select-model-unpaid.tsx +++ b/packages/app/src/components/dialog-select-model-unpaid.tsx @@ -34,11 +34,14 @@ export const DialogSelectModelUnpaid: Component = () => { }) return ( - -
+ +
{language.t("dialog.model.unpaid.freeModels.title")}
(listRef = ref)} items={local.model.list} current={local.model.current()} @@ -77,7 +80,7 @@ export const DialogSelectModelUnpaid: Component = () => { )}
-
+
{language.t("dialog.model.unpaid.addMore.title")}
From 30969dc33e20858a9b7773aa19ad345335a644c1 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 29 Jan 2026 21:51:26 -0500 Subject: [PATCH 02/33] ci: cache apt packages to reduce CI build times on ubuntu --- .github/workflows/publish.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3924ad49148..8e9a44bb2e6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -124,6 +124,15 @@ jobs: - uses: ./.github/actions/setup-bun + - name: Cache apt packages + if: contains(matrix.settings.host, 'ubuntu') + uses: actions/cache@v4 + with: + path: /var/cache/apt/archives + key: ${{ runner.os }}-${{ matrix.settings.target }}-apt-${{ hashFiles('.github/workflows/publish.yml') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.settings.target }}-apt- + - name: install dependencies (ubuntu only) if: contains(matrix.settings.host, 'ubuntu') run: | @@ -230,6 +239,14 @@ jobs: name: opencode-cli path: packages/opencode/dist + - name: Cache apt packages (AUR) + uses: actions/cache@v4 + with: + path: /var/cache/apt/archives + key: ${{ runner.os }}-apt-aur-${{ hashFiles('.github/workflows/publish.yml') }} + restore-keys: | + ${{ runner.os }}-apt-aur- + - name: Setup SSH for AUR run: | sudo apt-get update From 5cfb5fdd0664632956056a1004ab1857a8eb3673 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 29 Jan 2026 23:07:58 -0500 Subject: [PATCH 03/33] ci: add container build workflow Add prebuilt build images and a publish workflow to speed CI by reusing heavy dependencies. --- .github/workflows/containers.yml | 38 ++++++++++++++++++++++ packages/containers/README.md | 36 ++++++++++++++++++++ packages/containers/base/Dockerfile | 18 ++++++++++ packages/containers/bun-node/Dockerfile | 22 +++++++++++++ packages/containers/publish/Dockerfile | 10 ++++++ packages/containers/rust/Dockerfile | 13 ++++++++ packages/containers/script/build.ts | 25 ++++++++++++++ packages/containers/tauri-linux/Dockerfile | 12 +++++++ packages/containers/tsconfig.json | 8 +++++ 9 files changed, 182 insertions(+) create mode 100644 .github/workflows/containers.yml create mode 100644 packages/containers/README.md create mode 100644 packages/containers/base/Dockerfile create mode 100644 packages/containers/bun-node/Dockerfile create mode 100644 packages/containers/publish/Dockerfile create mode 100644 packages/containers/rust/Dockerfile create mode 100644 packages/containers/script/build.ts create mode 100644 packages/containers/tauri-linux/Dockerfile create mode 100644 packages/containers/tsconfig.json diff --git a/.github/workflows/containers.yml b/.github/workflows/containers.yml new file mode 100644 index 00000000000..0ce64066573 --- /dev/null +++ b/.github/workflows/containers.yml @@ -0,0 +1,38 @@ +name: containers + +on: + push: + branches: + - dev + paths: + - packages/containers/** + - .github/workflows/containers.yml + workflow_dispatch: + +permissions: + contents: read + packages: write + +jobs: + build: + runs-on: blacksmith-4vcpu-ubuntu-2404 + env: + REGISTRY: ghcr.io/${{ github.repository_owner }} + TAG: "24.04" + steps: + - uses: actions/checkout@v4 + + - uses: ./.github/actions/setup-bun + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push containers + run: bun ./packages/containers/script/build.ts --push + env: + REGISTRY: ${{ env.REGISTRY }} + TAG: ${{ env.TAG }} diff --git a/packages/containers/README.md b/packages/containers/README.md new file mode 100644 index 00000000000..42190f0ba65 --- /dev/null +++ b/packages/containers/README.md @@ -0,0 +1,36 @@ +# CI containers + +Prebuilt images intended to speed up GitHub Actions jobs by baking in +large, slow-to-install dependencies. These are designed for Linux jobs +that can use `job.container` in workflows. + +Images + +- `base`: Ubuntu 24.04 with common build tools and utilities +- `bun-node`: `base` plus Bun and Node.js 24 +- `rust`: `bun-node` plus Rust (stable, minimal profile) +- `tauri-linux`: `rust` plus Tauri Linux build dependencies +- `publish`: `bun-node` plus Docker CLI and AUR tooling + +Build + +``` +REGISTRY=ghcr.io/anomalyco TAG=24.04 bun ./packages/containers/script/build.ts +``` + +Workflow usage + +``` +jobs: + build-cli: + runs-on: ubuntu-latest + container: + image: ghcr.io/anomalyco/build/bun-node:24.04 +``` + +Notes + +- These images only help Linux jobs. macOS and Windows jobs cannot run + inside Linux containers. +- If a job uses Docker Buildx, the container needs access to the host + Docker daemon (or `docker-in-docker` with privileged mode). diff --git a/packages/containers/base/Dockerfile b/packages/containers/base/Dockerfile new file mode 100644 index 00000000000..a81f4baa22a --- /dev/null +++ b/packages/containers/base/Dockerfile @@ -0,0 +1,18 @@ +FROM ubuntu:24.04 + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + git \ + jq \ + openssh-client \ + pkg-config \ + python3 \ + unzip \ + xz-utils \ + zip \ + && rm -rf /var/lib/apt/lists/* diff --git a/packages/containers/bun-node/Dockerfile b/packages/containers/bun-node/Dockerfile new file mode 100644 index 00000000000..69c78039646 --- /dev/null +++ b/packages/containers/bun-node/Dockerfile @@ -0,0 +1,22 @@ +ARG REGISTRY=ghcr.io/anomalyco +FROM ${REGISTRY}/build/base:24.04 + +ARG NODE_VERSION=24.4.0 +ARG BUN_VERSION=1.2.4 + +ENV BUN_INSTALL=/opt/bun +ENV PATH=/opt/bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +RUN set -euo pipefail; \ + arch=$(uname -m); \ + node_arch=x64; \ + if [ "$arch" = "aarch64" ]; then node_arch=arm64; fi; \ + curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${node_arch}.tar.xz" \ + | tar -xJf - -C /usr/local --strip-components=1; \ + corepack enable + +RUN set -euo pipefail; \ + curl -fsSL https://bun.sh/install | bash -s -- "bun-v${BUN_VERSION}"; \ + bun --version; \ + node --version; \ + npm --version diff --git a/packages/containers/publish/Dockerfile b/packages/containers/publish/Dockerfile new file mode 100644 index 00000000000..4780d22740f --- /dev/null +++ b/packages/containers/publish/Dockerfile @@ -0,0 +1,10 @@ +ARG REGISTRY=ghcr.io/anomalyco +FROM ${REGISTRY}/build/bun-node:24.04 + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + docker.io \ + pacman-package-manager \ + && rm -rf /var/lib/apt/lists/* diff --git a/packages/containers/rust/Dockerfile b/packages/containers/rust/Dockerfile new file mode 100644 index 00000000000..533f348be76 --- /dev/null +++ b/packages/containers/rust/Dockerfile @@ -0,0 +1,13 @@ +ARG REGISTRY=ghcr.io/anomalyco +FROM ${REGISTRY}/build/bun-node:24.04 + +ARG RUST_TOOLCHAIN=stable + +ENV CARGO_HOME=/opt/cargo +ENV RUSTUP_HOME=/opt/rustup +ENV PATH=/opt/cargo/bin:/opt/bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +RUN set -euo pipefail; \ + curl -fsSL https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain "${RUST_TOOLCHAIN}"; \ + rustc --version; \ + cargo --version diff --git a/packages/containers/script/build.ts b/packages/containers/script/build.ts new file mode 100644 index 00000000000..18ae328e1c8 --- /dev/null +++ b/packages/containers/script/build.ts @@ -0,0 +1,25 @@ +#!/usr/bin/env bun + +import { $ } from "bun" + +const dir = new URL("..", import.meta.url).pathname +process.chdir(dir) + +const reg = process.env.REGISTRY ?? "ghcr.io/anomalyco" +const tag = process.env.TAG ?? "24.04" +const push = process.argv.includes("--push") || process.env.PUSH === "1" + +const images = ["base", "bun-node", "rust", "tauri-linux", "publish"] + +for (const name of images) { + const image = `${reg}/build/${name}:${tag}` + const file = `packages/containers/${name}/Dockerfile` + const arg = name === "base" ? "" : `--build-arg REGISTRY=${reg}` + const cmd = `docker build -f ${file} -t ${image} ${arg} .` + console.log(cmd) + await $`${cmd}` + + if (push) { + await $`docker push ${image}` + } +} diff --git a/packages/containers/tauri-linux/Dockerfile b/packages/containers/tauri-linux/Dockerfile new file mode 100644 index 00000000000..9f67a280498 --- /dev/null +++ b/packages/containers/tauri-linux/Dockerfile @@ -0,0 +1,12 @@ +ARG REGISTRY=ghcr.io/anomalyco +FROM ${REGISTRY}/build/rust:24.04 + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + libappindicator3-dev \ + libwebkit2gtk-4.1-dev \ + librsvg2-dev \ + patchelf \ + && rm -rf /var/lib/apt/lists/* diff --git a/packages/containers/tsconfig.json b/packages/containers/tsconfig.json new file mode 100644 index 00000000000..00ef1254685 --- /dev/null +++ b/packages/containers/tsconfig.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "@tsconfig/bun/tsconfig.json", + "compilerOptions": { + "lib": ["ESNext", "DOM", "DOM.Iterable"], + "noUncheckedIndexedAccess": false + } +} From 71d280d570ea77c9330942722b0e34383de831d9 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 29 Jan 2026 23:10:50 -0500 Subject: [PATCH 04/33] ci: fix container build script Invoke docker build with Bun shell so commands run correctly, and document default automation behavior. --- AGENTS.md | 1 + packages/containers/script/build.ts | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c3f8e50d05c..8cfe768da39 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,7 @@ - To regenerate the JavaScript SDK, run `./packages/sdk/js/script/build.ts`. - ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE. - The default branch in this repo is `dev`. +- Prefer automation: execute requested actions without confirmation unless blocked by missing info or safety/irreversibility. ## Style Guide diff --git a/packages/containers/script/build.ts b/packages/containers/script/build.ts index 18ae328e1c8..8fd6a7d4db2 100644 --- a/packages/containers/script/build.ts +++ b/packages/containers/script/build.ts @@ -15,9 +15,12 @@ for (const name of images) { const image = `${reg}/build/${name}:${tag}` const file = `packages/containers/${name}/Dockerfile` const arg = name === "base" ? "" : `--build-arg REGISTRY=${reg}` - const cmd = `docker build -f ${file} -t ${image} ${arg} .` - console.log(cmd) - await $`${cmd}` + console.log(`docker build -f ${file} -t ${image} ${arg} .`) + if (arg) { + await $`docker build -f ${file} -t ${image} --build-arg REGISTRY=${reg} .` + } else { + await $`docker build -f ${file} -t ${image} .` + } if (push) { await $`docker push ${image}` From 5ea1042ffba32b4e1b66e8944b5ec8c3899f5674 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 29 Jan 2026 23:13:07 -0500 Subject: [PATCH 05/33] ci --- packages/containers/bun-node/Dockerfile | 2 +- packages/containers/script/build.ts | 25 ++++++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/containers/bun-node/Dockerfile b/packages/containers/bun-node/Dockerfile index 69c78039646..31a3bb547a0 100644 --- a/packages/containers/bun-node/Dockerfile +++ b/packages/containers/bun-node/Dockerfile @@ -2,7 +2,7 @@ ARG REGISTRY=ghcr.io/anomalyco FROM ${REGISTRY}/build/base:24.04 ARG NODE_VERSION=24.4.0 -ARG BUN_VERSION=1.2.4 +ARG BUN_VERSION=1.3.5 ENV BUN_INSTALL=/opt/bun ENV PATH=/opt/bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin diff --git a/packages/containers/script/build.ts b/packages/containers/script/build.ts index 8fd6a7d4db2..0ace107310a 100644 --- a/packages/containers/script/build.ts +++ b/packages/containers/script/build.ts @@ -2,25 +2,36 @@ import { $ } from "bun" -const dir = new URL("..", import.meta.url).pathname -process.chdir(dir) +const rootDir = new URL("../../..", import.meta.url).pathname +process.chdir(rootDir) const reg = process.env.REGISTRY ?? "ghcr.io/anomalyco" const tag = process.env.TAG ?? "24.04" const push = process.argv.includes("--push") || process.env.PUSH === "1" +const root = new URL("package.json", new URL(rootDir)).pathname +const pkg = await Bun.file(root).json() +const manager = pkg.packageManager ?? "" +const bun = manager.startsWith("bun@") ? manager.slice(4) : "" +if (!bun) throw new Error("packageManager must be bun@") + const images = ["base", "bun-node", "rust", "tauri-linux", "publish"] for (const name of images) { const image = `${reg}/build/${name}:${tag}` const file = `packages/containers/${name}/Dockerfile` - const arg = name === "base" ? "" : `--build-arg REGISTRY=${reg}` - console.log(`docker build -f ${file} -t ${image} ${arg} .`) - if (arg) { - await $`docker build -f ${file} -t ${image} --build-arg REGISTRY=${reg} .` - } else { + if (name === "base") { + console.log(`docker build -f ${file} -t ${image} .`) await $`docker build -f ${file} -t ${image} .` } + if (name === "bun-node") { + console.log(`docker build -f ${file} -t ${image} --build-arg REGISTRY=${reg} --build-arg BUN_VERSION=${bun} .`) + await $`docker build -f ${file} -t ${image} --build-arg REGISTRY=${reg} --build-arg BUN_VERSION=${bun} .` + } + if (name !== "base" && name !== "bun-node") { + console.log(`docker build -f ${file} -t ${image} --build-arg REGISTRY=${reg} .`) + await $`docker build -f ${file} -t ${image} --build-arg REGISTRY=${reg} .` + } if (push) { await $`docker push ${image}` From 849f488744ae6ba6b1f647e2ace9485bf1e3f214 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 29 Jan 2026 23:15:12 -0500 Subject: [PATCH 06/33] ci --- packages/containers/script/build.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/containers/script/build.ts b/packages/containers/script/build.ts index 0ace107310a..ff259f2ab82 100644 --- a/packages/containers/script/build.ts +++ b/packages/containers/script/build.ts @@ -1,15 +1,17 @@ #!/usr/bin/env bun import { $ } from "bun" +import path from "path" +import { fileURLToPath } from "url" -const rootDir = new URL("../../..", import.meta.url).pathname +const rootDir = fileURLToPath(new URL("../../..", import.meta.url)) process.chdir(rootDir) const reg = process.env.REGISTRY ?? "ghcr.io/anomalyco" const tag = process.env.TAG ?? "24.04" const push = process.argv.includes("--push") || process.env.PUSH === "1" -const root = new URL("package.json", new URL(rootDir)).pathname +const root = path.join(rootDir, "package.json") const pkg = await Bun.file(root).json() const manager = pkg.packageManager ?? "" const bun = manager.startsWith("bun@") ? manager.slice(4) : "" From cd664a189b7c1de5232b58c180fca72b86adada6 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 29 Jan 2026 23:17:57 -0500 Subject: [PATCH 07/33] ci --- .github/workflows/containers.yml | 7 ++++ packages/containers/README.md | 2 + packages/containers/bun-node/Dockerfile | 2 + packages/containers/script/build.ts | 50 +++++++++++++++++++++---- 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/.github/workflows/containers.yml b/.github/workflows/containers.yml index 0ce64066573..c7df066d41c 100644 --- a/.github/workflows/containers.yml +++ b/.github/workflows/containers.yml @@ -7,6 +7,7 @@ on: paths: - packages/containers/** - .github/workflows/containers.yml + - package.json workflow_dispatch: permissions: @@ -24,6 +25,12 @@ jobs: - uses: ./.github/actions/setup-bun + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Login to GHCR uses: docker/login-action@v3 with: diff --git a/packages/containers/README.md b/packages/containers/README.md index 42190f0ba65..a1cfe60db3e 100644 --- a/packages/containers/README.md +++ b/packages/containers/README.md @@ -16,6 +16,7 @@ Build ``` REGISTRY=ghcr.io/anomalyco TAG=24.04 bun ./packages/containers/script/build.ts +REGISTRY=ghcr.io/anomalyco TAG=24.04 bun ./packages/containers/script/build.ts --push ``` Workflow usage @@ -32,5 +33,6 @@ Notes - These images only help Linux jobs. macOS and Windows jobs cannot run inside Linux containers. +- `--push` publishes multi-arch (amd64 + arm64) images using Buildx. - If a job uses Docker Buildx, the container needs access to the host Docker daemon (or `docker-in-docker` with privileged mode). diff --git a/packages/containers/bun-node/Dockerfile b/packages/containers/bun-node/Dockerfile index 31a3bb547a0..e6cad9c2725 100644 --- a/packages/containers/bun-node/Dockerfile +++ b/packages/containers/bun-node/Dockerfile @@ -1,6 +1,8 @@ ARG REGISTRY=ghcr.io/anomalyco FROM ${REGISTRY}/build/base:24.04 +SHELL ["/bin/bash", "-lc"] + ARG NODE_VERSION=24.4.0 ARG BUN_VERSION=1.3.5 diff --git a/packages/containers/script/build.ts b/packages/containers/script/build.ts index ff259f2ab82..6b880e7a5b9 100644 --- a/packages/containers/script/build.ts +++ b/packages/containers/script/build.ts @@ -19,23 +19,59 @@ if (!bun) throw new Error("packageManager must be bun@") const images = ["base", "bun-node", "rust", "tauri-linux", "publish"] +const setup = async () => { + if (!push) return + const list = await $`docker buildx ls`.text() + if (list.includes("opencode")) { + await $`docker buildx use opencode` + return + } + await $`docker buildx create --name opencode --use` +} + +await setup() + +const platform = "linux/amd64,linux/arm64" + for (const name of images) { const image = `${reg}/build/${name}:${tag}` const file = `packages/containers/${name}/Dockerfile` if (name === "base") { - console.log(`docker build -f ${file} -t ${image} .`) - await $`docker build -f ${file} -t ${image} .` + if (push) { + console.log(`docker buildx build --platform ${platform} -f ${file} -t ${image} --push .`) + await $`docker buildx build --platform ${platform} -f ${file} -t ${image} --push .` + } + if (!push) { + console.log(`docker build -f ${file} -t ${image} .`) + await $`docker build -f ${file} -t ${image} .` + } } if (name === "bun-node") { - console.log(`docker build -f ${file} -t ${image} --build-arg REGISTRY=${reg} --build-arg BUN_VERSION=${bun} .`) - await $`docker build -f ${file} -t ${image} --build-arg REGISTRY=${reg} --build-arg BUN_VERSION=${bun} .` + if (push) { + console.log( + `docker buildx build --platform ${platform} -f ${file} -t ${image} --build-arg REGISTRY=${reg} --build-arg BUN_VERSION=${bun} --push .`, + ) + await $`docker buildx build --platform ${platform} -f ${file} -t ${image} --build-arg REGISTRY=${reg} --build-arg BUN_VERSION=${bun} --push .` + } + if (!push) { + console.log(`docker build -f ${file} -t ${image} --build-arg REGISTRY=${reg} --build-arg BUN_VERSION=${bun} .`) + await $`docker build -f ${file} -t ${image} --build-arg REGISTRY=${reg} --build-arg BUN_VERSION=${bun} .` + } } if (name !== "base" && name !== "bun-node") { - console.log(`docker build -f ${file} -t ${image} --build-arg REGISTRY=${reg} .`) - await $`docker build -f ${file} -t ${image} --build-arg REGISTRY=${reg} .` + if (push) { + console.log( + `docker buildx build --platform ${platform} -f ${file} -t ${image} --build-arg REGISTRY=${reg} --push .`, + ) + await $`docker buildx build --platform ${platform} -f ${file} -t ${image} --build-arg REGISTRY=${reg} --push .` + } + if (!push) { + console.log(`docker build -f ${file} -t ${image} --build-arg REGISTRY=${reg} .`) + await $`docker build -f ${file} -t ${image} --build-arg REGISTRY=${reg} .` + } } if (push) { - await $`docker push ${image}` + console.log(`pushed ${image}`) } } From 2d3c7a0f24754ea0643d9bfd6dec2df39623763f Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 29 Jan 2026 23:49:53 -0500 Subject: [PATCH 08/33] ci --- .../actions/setup-git-committer/action.yml | 42 +++++++++++++++++++ .github/workflows/generate.yml | 12 +++--- 2 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 .github/actions/setup-git-committer/action.yml diff --git a/.github/actions/setup-git-committer/action.yml b/.github/actions/setup-git-committer/action.yml new file mode 100644 index 00000000000..39374e167df --- /dev/null +++ b/.github/actions/setup-git-committer/action.yml @@ -0,0 +1,42 @@ +name: "Setup Git Committer" +description: "Create app token and configure git user" +inputs: + opencode-app-id: + description: "OpenCode GitHub App ID" + required: true + opencode-app-secret: + description: "OpenCode GitHub App private key" + required: true +outputs: + token: + description: "GitHub App token" + value: ${{ steps.app-token.outputs.token }} + app-slug: + description: "GitHub App slug" + value: ${{ steps.app-token.outputs.app-slug }} + user-id: + description: "GitHub App user id" + value: ${{ steps.get-user-id.outputs.user-id }} +runs: + using: "composite" + steps: + - name: Create app token + id: app-token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ inputs.opencode-app-id }} + private-key: ${{ inputs.opencode-app-secret }} + + - name: Get GitHub App user id + id: get-user-id + run: | + echo "user-id=$(gh api \"/users/${{ steps.app-token.outputs.app-slug }}[bot]\" --jq .id)" >> "$GITHUB_OUTPUT" + shell: bash + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + + - name: Configure git user + run: | + git config --global user.name "${{ steps.app-token.outputs.app-slug }}[bot]" + git config --global user.email "${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com" + shell: bash diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index cbbab479e14..28aaa16f733 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -24,17 +24,17 @@ jobs: - name: Setup Bun uses: ./.github/actions/setup-bun + - name: Setup git committer + uses: ./.github/actions/setup-git-committer + with: + opencode-app-id: ${{ vars.OPENCODE_APP_ID }} + opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} + - name: Generate run: ./script/generate.ts - name: Commit and push run: | - if [ -z "$(git status --porcelain)" ]; then - echo "No changes to commit" - exit 0 - fi - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" git add -A git commit -m "chore: generate" git push origin HEAD:${{ github.ref_name }} --no-verify From 3ac05201c6501e21ad0bcd5295498614d570e5fa Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 29 Jan 2026 23:52:08 -0500 Subject: [PATCH 09/33] ci --- .github/actions/setup-git-committer/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-git-committer/action.yml b/.github/actions/setup-git-committer/action.yml index 39374e167df..9af1c3d1a35 100644 --- a/.github/actions/setup-git-committer/action.yml +++ b/.github/actions/setup-git-committer/action.yml @@ -30,7 +30,7 @@ runs: - name: Get GitHub App user id id: get-user-id run: | - echo "user-id=$(gh api \"/users/${{ steps.app-token.outputs.app-slug }}[bot]\" --jq .id)" >> "$GITHUB_OUTPUT" + echo "user-id=$(gh api \"/users/${{ steps.app-token.outputs.app-slug }}%5Bbot%5D\" --jq .id)" >> "$GITHUB_OUTPUT" shell: bash env: GH_TOKEN: ${{ steps.app-token.outputs.token }} From 3fef4901875137a643c89b68e231dd77ff1cbb5c Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 29 Jan 2026 23:55:48 -0500 Subject: [PATCH 10/33] ci --- .../actions/setup-git-committer/action.yml | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/.github/actions/setup-git-committer/action.yml b/.github/actions/setup-git-committer/action.yml index 9af1c3d1a35..4367996bd6d 100644 --- a/.github/actions/setup-git-committer/action.yml +++ b/.github/actions/setup-git-committer/action.yml @@ -10,33 +10,23 @@ inputs: outputs: token: description: "GitHub App token" - value: ${{ steps.app-token.outputs.token }} + value: ${{ steps.apptoken.outputs.token }} app-slug: description: "GitHub App slug" - value: ${{ steps.app-token.outputs.app-slug }} - user-id: - description: "GitHub App user id" - value: ${{ steps.get-user-id.outputs.user-id }} + value: ${{ steps.apptoken.outputs.app-slug }} runs: using: "composite" steps: - name: Create app token - id: app-token + id: apptoken uses: actions/create-github-app-token@v2 with: app-id: ${{ inputs.opencode-app-id }} private-key: ${{ inputs.opencode-app-secret }} - - name: Get GitHub App user id - id: get-user-id - run: | - echo "user-id=$(gh api \"/users/${{ steps.app-token.outputs.app-slug }}%5Bbot%5D\" --jq .id)" >> "$GITHUB_OUTPUT" - shell: bash - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - - name: Configure git user run: | - git config --global user.name "${{ steps.app-token.outputs.app-slug }}[bot]" - git config --global user.email "${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com" + slug="${{ steps.apptoken.outputs.app-slug }}" + git config --global user.name "${slug}[bot]" + git config --global user.email "${slug}[bot]@users.noreply.github.com" shell: bash From 908350c2ea94d657777387c041cf47e313d447c4 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 29 Jan 2026 23:56:56 -0500 Subject: [PATCH 11/33] ci --- .github/workflows/generate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index 28aaa16f733..15c99f4024c 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -36,7 +36,7 @@ jobs: - name: Commit and push run: | git add -A - git commit -m "chore: generate" + git commit -m "chore: generate" --allow-empty git push origin HEAD:${{ github.ref_name }} --no-verify # if ! git push origin HEAD:${{ github.event.pull_request.head.ref || github.ref_name }} --no-verify; then # echo "" From 1ab4bbc275c0d9a4b7bc5bab1958ba0506a4852b Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Thu, 29 Jan 2026 23:58:39 -0500 Subject: [PATCH 12/33] ci --- .github/workflows/generate.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index 15c99f4024c..39263200c47 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -35,8 +35,12 @@ jobs: - name: Commit and push run: | + if [ -z "$(git status --porcelain)" ]; then + echo "No changes to commit" + exit 0 + fi git add -A - git commit -m "chore: generate" --allow-empty + git commit -m "chore: generate" git push origin HEAD:${{ github.ref_name }} --no-verify # if ! git push origin HEAD:${{ github.event.pull_request.head.ref || github.ref_name }} --no-verify; then # echo "" From 5d0122b5a976105e5924ba99e7b17a814c718947 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 30 Jan 2026 00:04:51 -0500 Subject: [PATCH 13/33] ci --- .github/workflows/publish.yml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8e9a44bb2e6..73755153aac 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -37,6 +37,13 @@ jobs: steps: - uses: actions/checkout@v3 - uses: ./.github/actions/setup-bun + + - name: Setup git committer + id: committer + uses: ./.github/actions/setup-git-committer + with: + opencode-app-id: ${{ vars.OPENCODE_APP_ID }} + opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - id: version run: | ./script/version.ts @@ -155,7 +162,7 @@ jobs: bun ./scripts/prepare.ts env: OPENCODE_VERSION: ${{ needs.version.outputs.version }} - GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.committer.outputs.token }} RUST_TARGET: ${{ matrix.settings.target }} GH_TOKEN: ${{ github.token }} GITHUB_RUN_ID: ${{ github.run_id }} @@ -228,11 +235,16 @@ jobs: node-version: "24" registry-url: "https://registry.npmjs.org" - - name: Setup Git Identity + - name: Setup git committer + id: committer + uses: ./.github/actions/setup-git-committer + with: + opencode-app-id: ${{ vars.OPENCODE_APP_ID }} + opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} + + - name: Setup Git remote 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 }} + git remote set-url origin https://x-access-token:${{ steps.committer.outputs.token }}@github.com/${{ github.repository }} - uses: actions/download-artifact@v4 with: @@ -263,6 +275,6 @@ jobs: OPENCODE_VERSION: ${{ needs.version.outputs.version }} OPENCODE_RELEASE: ${{ needs.version.outputs.release }} AUR_KEY: ${{ secrets.AUR_KEY }} - GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.committer.outputs.token }} OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} NPM_CONFIG_PROVENANCE: false From b28891473f16362b9d97d3825e6c969c4988d970 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 30 Jan 2026 00:05:05 -0500 Subject: [PATCH 14/33] ci --- .github/workflows/generate.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index 39263200c47..15c99f4024c 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -35,12 +35,8 @@ jobs: - name: Commit and push run: | - if [ -z "$(git status --porcelain)" ]; then - echo "No changes to commit" - exit 0 - fi git add -A - git commit -m "chore: generate" + git commit -m "chore: generate" --allow-empty git push origin HEAD:${{ github.ref_name }} --no-verify # if ! git push origin HEAD:${{ github.event.pull_request.head.ref || github.ref_name }} --no-verify; then # echo "" From b109ab78303829943c7c229e9126983601d8bd6d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 30 Jan 2026 00:07:56 -0500 Subject: [PATCH 15/33] ci --- .github/actions/setup-git-committer/action.yml | 5 +++++ .github/workflows/generate.yml | 1 + .github/workflows/publish.yml | 4 ---- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/actions/setup-git-committer/action.yml b/.github/actions/setup-git-committer/action.yml index 4367996bd6d..e039119fd5e 100644 --- a/.github/actions/setup-git-committer/action.yml +++ b/.github/actions/setup-git-committer/action.yml @@ -30,3 +30,8 @@ runs: git config --global user.name "${slug}[bot]" git config --global user.email "${slug}[bot]@users.noreply.github.com" shell: bash + + - name: Configure git remote + run: | + git remote set-url origin https://x-access-token:${{ steps.apptoken.outputs.token }}@github.com/${{ github.repository }} + shell: bash diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index 15c99f4024c..cae20a24438 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -25,6 +25,7 @@ jobs: uses: ./.github/actions/setup-bun - name: Setup git committer + id: committer uses: ./.github/actions/setup-git-committer with: opencode-app-id: ${{ vars.OPENCODE_APP_ID }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 73755153aac..dd0487c4048 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -242,10 +242,6 @@ jobs: opencode-app-id: ${{ vars.OPENCODE_APP_ID }} opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - - name: Setup Git remote - run: | - git remote set-url origin https://x-access-token:${{ steps.committer.outputs.token }}@github.com/${{ github.repository }} - - uses: actions/download-artifact@v4 with: name: opencode-cli From b5e5d4c92f8a6468831c8ab1101048f7d1ad3c2c Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 30 Jan 2026 00:10:44 -0500 Subject: [PATCH 16/33] ci --- .github/actions/setup-git-committer/action.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/actions/setup-git-committer/action.yml b/.github/actions/setup-git-committer/action.yml index e039119fd5e..e5a57206521 100644 --- a/.github/actions/setup-git-committer/action.yml +++ b/.github/actions/setup-git-committer/action.yml @@ -31,6 +31,11 @@ runs: git config --global user.email "${slug}[bot]@users.noreply.github.com" shell: bash + - name: Clear checkout auth + run: | + git config --local --unset-all http.https://github.com/.extraheader || true + shell: bash + - name: Configure git remote run: | git remote set-url origin https://x-access-token:${{ steps.apptoken.outputs.token }}@github.com/${{ github.repository }} From 36041c000078c9e4e7ccc38707785e0eb757860b Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 30 Jan 2026 00:15:21 -0500 Subject: [PATCH 17/33] ci --- .github/workflows/generate.yml | 8 +-- .github/workflows/publish.yml | 126 ++++++++++++++++++++++++++------- 2 files changed, 103 insertions(+), 31 deletions(-) diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index cae20a24438..3bb8f364d6b 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -16,10 +16,6 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} - ref: ${{ github.event.pull_request.head.ref || github.ref_name }} - name: Setup Bun uses: ./.github/actions/setup-bun @@ -36,6 +32,10 @@ jobs: - name: Commit and push run: | + if [ -z "$(git status --porcelain)" ]; then + echo "No changes to commit" + exit 0 + fi git add -A git commit -m "chore: generate" --allow-empty git push origin HEAD:${{ github.ref_name }} --no-verify diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index dd0487c4048..17942a59d38 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -60,6 +60,8 @@ jobs: needs: version runs-on: blacksmith-4vcpu-ubuntu-2404 if: github.repository == 'anomalyco/opencode' + container: + image: ghcr.io/anomalyco/build/bun-node:24.04 steps: - uses: actions/checkout@v3 with: @@ -84,6 +86,102 @@ jobs: outputs: version: ${{ needs.version.outputs.version }} + build-tauri-linux: + needs: + - build-cli + - version + continue-on-error: false + strategy: + fail-fast: false + matrix: + settings: + - host: blacksmith-4vcpu-ubuntu-2404 + target: x86_64-unknown-linux-gnu + - host: blacksmith-4vcpu-ubuntu-2404-arm + target: aarch64-unknown-linux-gnu + runs-on: ${{ matrix.settings.host }} + container: + image: ghcr.io/anomalyco/build/tauri-linux:24.04 + steps: + - uses: actions/checkout@v3 + with: + fetch-tags: true + + - uses: apple-actions/import-codesign-certs@v2 + if: ${{ runner.os == 'macOS' }} + with: + keychain: build + p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }} + p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + + - name: Verify Certificate + if: ${{ runner.os == 'macOS' }} + run: | + CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application") + CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}') + echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV + echo "Certificate imported." + + - name: Setup Apple API Key + if: ${{ runner.os == 'macOS' }} + run: | + echo "${{ secrets.APPLE_API_KEY_PATH }}" > $RUNNER_TEMP/apple-api-key.p8 + + - uses: ./.github/actions/setup-bun + + - name: install Rust stable + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.settings.target }} + + - uses: Swatinem/rust-cache@v2 + with: + workspaces: packages/desktop/src-tauri + shared-key: ${{ matrix.settings.target }} + + - name: Prepare + run: | + cd packages/desktop + bun ./scripts/prepare.ts + env: + OPENCODE_VERSION: ${{ needs.version.outputs.version }} + GITHUB_TOKEN: ${{ steps.committer.outputs.token }} + RUST_TARGET: ${{ matrix.settings.target }} + GH_TOKEN: ${{ github.token }} + GITHUB_RUN_ID: ${{ github.run_id }} + + # Fixes AppImage build issues, can be removed when https://github.com/tauri-apps/tauri/pull/12491 is released + - name: Install tauri-cli from portable appimage branch + run: | + cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/truly-portable-appimage --force + echo "Installed tauri-cli version:" + cargo tauri --version + + - name: Build and upload artifacts + uses: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a + timeout-minutes: 60 + 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.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 + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }} + APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} + APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} + APPLE_API_KEY_PATH: ${{ runner.temp }}/apple-api-key.p8 + build-tauri: needs: - build-cli @@ -99,10 +197,6 @@ jobs: target: aarch64-apple-darwin - host: blacksmith-4vcpu-windows-2025 target: x86_64-pc-windows-msvc - - host: blacksmith-4vcpu-ubuntu-2404 - target: x86_64-unknown-linux-gnu - - host: blacksmith-4vcpu-ubuntu-2404-arm - target: aarch64-unknown-linux-gnu runs-on: ${{ matrix.settings.host }} steps: - uses: actions/checkout@v3 @@ -131,21 +225,6 @@ jobs: - uses: ./.github/actions/setup-bun - - name: Cache apt packages - if: contains(matrix.settings.host, 'ubuntu') - uses: actions/cache@v4 - with: - path: /var/cache/apt/archives - key: ${{ runner.os }}-${{ matrix.settings.target }}-apt-${{ hashFiles('.github/workflows/publish.yml') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.settings.target }}-apt- - - - name: install dependencies (ubuntu only) - if: contains(matrix.settings.host, 'ubuntu') - run: | - sudo apt-get update - sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf - - name: install Rust stable uses: dtolnay/rust-toolchain@stable with: @@ -167,14 +246,6 @@ jobs: GH_TOKEN: ${{ github.token }} GITHUB_RUN_ID: ${{ github.run_id }} - # Fixes AppImage build issues, can be removed when https://github.com/tauri-apps/tauri/pull/12491 is released - - name: Install tauri-cli from portable appimage branch - if: contains(matrix.settings.host, 'ubuntu') - run: | - cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/truly-portable-appimage --force - echo "Installed tauri-cli version:" - cargo tauri --version - - name: Build and upload artifacts uses: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a timeout-minutes: 60 @@ -205,6 +276,7 @@ jobs: - version - build-cli - build-tauri + - build-tauri-linux runs-on: blacksmith-4vcpu-ubuntu-2404 steps: - uses: actions/checkout@v3 From 273e7b837986235ba9ec76830c1ba29c9c676068 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 30 Jan 2026 00:18:50 -0500 Subject: [PATCH 18/33] ci --- .github/workflows/publish.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 17942a59d38..4c6a0569621 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -67,6 +67,9 @@ jobs: with: fetch-tags: true + - name: Mark workspace as safe + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - uses: ./.github/actions/setup-bun - name: Build @@ -107,6 +110,9 @@ jobs: with: fetch-tags: true + - name: Mark workspace as safe + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - uses: apple-actions/import-codesign-certs@v2 if: ${{ runner.os == 'macOS' }} with: From da7f45bd4c1af30ab241558156d7d1b6bbef71f5 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 30 Jan 2026 00:21:30 -0500 Subject: [PATCH 19/33] ci --- .github/workflows/publish.yml | 46 ++++++----------------------------- 1 file changed, 7 insertions(+), 39 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4c6a0569621..7562910f6ee 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -63,12 +63,10 @@ jobs: container: image: ghcr.io/anomalyco/build/bun-node:24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-tags: true - - - name: Mark workspace as safe - run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + set-safe-directory: true - uses: ./.github/actions/setup-bun @@ -106,39 +104,15 @@ jobs: container: image: ghcr.io/anomalyco/build/tauri-linux:24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-tags: true - - - name: Mark workspace as safe - run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - - - uses: apple-actions/import-codesign-certs@v2 - if: ${{ runner.os == 'macOS' }} - with: - keychain: build - p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }} - p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - - - name: Verify Certificate - if: ${{ runner.os == 'macOS' }} - run: | - CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application") - CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}') - echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV - echo "Certificate imported." - - - name: Setup Apple API Key - if: ${{ runner.os == 'macOS' }} - run: | - echo "${{ secrets.APPLE_API_KEY_PATH }}" > $RUNNER_TEMP/apple-api-key.p8 + set-safe-directory: true - uses: ./.github/actions/setup-bun - - name: install Rust stable - uses: dtolnay/rust-toolchain@stable - with: - targets: ${{ matrix.settings.target }} + - name: add Rust target + run: rustup target add ${{ matrix.settings.target }} - uses: Swatinem/rust-cache@v2 with: @@ -169,7 +143,7 @@ jobs: with: projectPath: packages/desktop uploadWorkflowArtifacts: true - tauriScript: ${{ (contains(matrix.settings.host, 'ubuntu') && 'cargo tauri') || '' }} + tauriScript: cargo tauri args: --target ${{ matrix.settings.target }} --config ./src-tauri/tauri.prod.conf.json --verbose updaterJsonPreferNsis: true releaseId: ${{ needs.version.outputs.release }} @@ -181,12 +155,6 @@ jobs: TAURI_BUNDLER_NEW_APPIMAGE_FORMAT: true TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} - APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} - APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }} - APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} - APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} - APPLE_API_KEY_PATH: ${{ runner.temp }}/apple-api-key.p8 build-tauri: needs: From e666ddb63039a764266a202b2b6ccc615ef8a395 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 30 Jan 2026 00:23:52 -0500 Subject: [PATCH 20/33] ci --- .github/workflows/publish.yml | 102 ++++++++++------------------------ 1 file changed, 28 insertions(+), 74 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7562910f6ee..dd0487c4048 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -60,13 +60,10 @@ jobs: needs: version runs-on: blacksmith-4vcpu-ubuntu-2404 if: github.repository == 'anomalyco/opencode' - container: - image: ghcr.io/anomalyco/build/bun-node:24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 with: fetch-tags: true - set-safe-directory: true - uses: ./.github/actions/setup-bun @@ -87,75 +84,6 @@ jobs: outputs: version: ${{ needs.version.outputs.version }} - build-tauri-linux: - needs: - - build-cli - - version - continue-on-error: false - strategy: - fail-fast: false - matrix: - settings: - - host: blacksmith-4vcpu-ubuntu-2404 - target: x86_64-unknown-linux-gnu - - host: blacksmith-4vcpu-ubuntu-2404-arm - target: aarch64-unknown-linux-gnu - runs-on: ${{ matrix.settings.host }} - container: - image: ghcr.io/anomalyco/build/tauri-linux:24.04 - steps: - - uses: actions/checkout@v4 - with: - fetch-tags: true - set-safe-directory: true - - - uses: ./.github/actions/setup-bun - - - name: add Rust target - run: rustup target add ${{ matrix.settings.target }} - - - uses: Swatinem/rust-cache@v2 - with: - workspaces: packages/desktop/src-tauri - shared-key: ${{ matrix.settings.target }} - - - name: Prepare - run: | - cd packages/desktop - bun ./scripts/prepare.ts - env: - OPENCODE_VERSION: ${{ needs.version.outputs.version }} - GITHUB_TOKEN: ${{ steps.committer.outputs.token }} - RUST_TARGET: ${{ matrix.settings.target }} - GH_TOKEN: ${{ github.token }} - GITHUB_RUN_ID: ${{ github.run_id }} - - # Fixes AppImage build issues, can be removed when https://github.com/tauri-apps/tauri/pull/12491 is released - - name: Install tauri-cli from portable appimage branch - run: | - cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/truly-portable-appimage --force - echo "Installed tauri-cli version:" - cargo tauri --version - - - name: Build and upload artifacts - uses: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a - timeout-minutes: 60 - with: - projectPath: packages/desktop - uploadWorkflowArtifacts: true - tauriScript: 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 - TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} - TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} - build-tauri: needs: - build-cli @@ -171,6 +99,10 @@ jobs: target: aarch64-apple-darwin - host: blacksmith-4vcpu-windows-2025 target: x86_64-pc-windows-msvc + - host: blacksmith-4vcpu-ubuntu-2404 + target: x86_64-unknown-linux-gnu + - host: blacksmith-4vcpu-ubuntu-2404-arm + target: aarch64-unknown-linux-gnu runs-on: ${{ matrix.settings.host }} steps: - uses: actions/checkout@v3 @@ -199,6 +131,21 @@ jobs: - uses: ./.github/actions/setup-bun + - name: Cache apt packages + if: contains(matrix.settings.host, 'ubuntu') + uses: actions/cache@v4 + with: + path: /var/cache/apt/archives + key: ${{ runner.os }}-${{ matrix.settings.target }}-apt-${{ hashFiles('.github/workflows/publish.yml') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.settings.target }}-apt- + + - name: install dependencies (ubuntu only) + if: contains(matrix.settings.host, 'ubuntu') + run: | + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf + - name: install Rust stable uses: dtolnay/rust-toolchain@stable with: @@ -220,6 +167,14 @@ jobs: GH_TOKEN: ${{ github.token }} GITHUB_RUN_ID: ${{ github.run_id }} + # Fixes AppImage build issues, can be removed when https://github.com/tauri-apps/tauri/pull/12491 is released + - name: Install tauri-cli from portable appimage branch + if: contains(matrix.settings.host, 'ubuntu') + run: | + cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/truly-portable-appimage --force + echo "Installed tauri-cli version:" + cargo tauri --version + - name: Build and upload artifacts uses: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a timeout-minutes: 60 @@ -250,7 +205,6 @@ jobs: - version - build-cli - build-tauri - - build-tauri-linux runs-on: blacksmith-4vcpu-ubuntu-2404 steps: - uses: actions/checkout@v3 From 015eda36ce155ed61bbf789c1fec79ced33d6d3f Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 30 Jan 2026 00:25:52 -0500 Subject: [PATCH 21/33] ci --- .github/workflows/publish.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index dd0487c4048..85343d9293c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -38,12 +38,6 @@ jobs: - uses: actions/checkout@v3 - uses: ./.github/actions/setup-bun - - name: Setup git committer - id: committer - uses: ./.github/actions/setup-git-committer - with: - opencode-app-id: ${{ vars.OPENCODE_APP_ID }} - opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} - id: version run: | ./script/version.ts From 66ec378680e420581e2b14a6b6558c61f606dbd6 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 30 Jan 2026 00:27:51 -0500 Subject: [PATCH 22/33] ci --- .github/workflows/nix-hashes.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/nix-hashes.yml b/.github/workflows/nix-hashes.yml index 63ab5618870..061b4ada8d9 100644 --- a/.github/workflows/nix-hashes.yml +++ b/.github/workflows/nix-hashes.yml @@ -36,14 +36,16 @@ jobs: ref: ${{ github.head_ref || github.ref_name }} repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} + - name: Setup git committer + id: committer + uses: ./.github/actions/setup-git-committer + with: + opencode-app-id: ${{ vars.OPENCODE_APP_ID }} + opencode-app-secret: ${{ secrets.OPENCODE_APP_SECRET }} + - name: Setup Nix uses: nixbuild/nix-quick-install-action@v34 - - name: Configure git - run: | - git config --global user.email "action@github.com" - git config --global user.name "Github Action" - - name: Pull latest changes env: TARGET_BRANCH: ${{ github.head_ref || github.ref_name }} From 9e0747c9b4ec41e7ba5dbe6891770cfa600f768d Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 30 Jan 2026 00:39:23 -0500 Subject: [PATCH 23/33] ci --- script/changelog-debug.ts | 85 +++++++++++++++++++++++++++++++++++++++ script/publish.ts | 5 ++- 2 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 script/changelog-debug.ts diff --git a/script/changelog-debug.ts b/script/changelog-debug.ts new file mode 100644 index 00000000000..1144d3b0cbc --- /dev/null +++ b/script/changelog-debug.ts @@ -0,0 +1,85 @@ +#!/usr/bin/env bun + +import { $ } from "bun" +import { parseArgs } from "util" +import { getLatestRelease } from "./changelog" + +const paths = [ + "packages/opencode", + "packages/sdk", + "packages/plugin", + "packages/desktop", + "packages/app", + "sdks/vscode", + "packages/extensions", + "github", +] + +const clean = (text: string) => text.split("\n").filter(Boolean) + +const ref = (value: string, head = false) => { + if (head && value === "HEAD") return value + if (value.startsWith("v")) return value + return `v${value}` +} + +const { values } = parseArgs({ + args: Bun.argv.slice(2), + options: { + from: { type: "string", short: "f" }, + to: { type: "string", short: "t", default: "HEAD" }, + base: { type: "string", short: "b", default: "origin/dev" }, + help: { type: "boolean", short: "h", default: false }, + }, +}) + +if (values.help) { + console.log(` +Usage: bun script/changelog-debug.ts [options] + +Options: + -f, --from Starting version (default: latest GitHub release) + -t, --to Ending ref (default: HEAD) + -b, --base Compare base for ahead/behind (default: origin/dev) + -h, --help Show this help message + +Examples: + bun script/changelog-debug.ts + bun script/changelog-debug.ts -f 1.0.200 -t dev + bun script/changelog-debug.ts -f 1.0.200 -t HEAD -b origin/dev +`) + process.exit(0) +} + +const to = values.to! +const from = values.from ?? (await getLatestRelease()) +const fromRef = ref(from) +const toRef = ref(to, true) + +console.log(`Debugging changelog range: ${fromRef} -> ${toRef}\n`) + +const [ahead, behind] = await $`git rev-list --left-right --count ${values.base}...HEAD` + .text() + .then((text) => text.trim().split("\t")) + +console.log(`Ahead/behind ${values.base}: ahead=${ahead ?? "0"} behind=${behind ?? "0"}`) + +const gh = await $`gh api "/repos/anomalyco/opencode/compare/${fromRef}...${toRef}" --jq '.commits[].sha'` + .text() + .then(clean) + +const localAll = await $`git log ${fromRef}..${toRef} --oneline --format="%H"`.text().then(clean) +const localFiltered = await $`git log ${fromRef}..${toRef} --oneline --format="%H" -- ${paths}`.text().then(clean) + +const ghSet = new Set(gh) +const missing = localFiltered.filter((hash) => !ghSet.has(hash)) + +console.log(`GitHub compare commits: ${gh.length}`) +console.log(`Local commits (all): ${localAll.length}`) +console.log(`Local commits (filtered paths): ${localFiltered.length}`) +console.log(`Filtered commits missing from GitHub compare: ${missing.length}`) + +if (missing.length > 0) { + console.log("\nMissing hashes (first 10):") + console.log(missing.slice(0, 10).join("\n")) +} diff --git a/script/publish.ts b/script/publish.ts index 23a6e5c9dfc..5fa84825b39 100755 --- a/script/publish.ts +++ b/script/publish.ts @@ -58,8 +58,6 @@ await $`bun install` await import(`../packages/sdk/js/script/build.ts`) 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}` @@ -67,6 +65,9 @@ if (Script.release) { 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)) + const previous = await getLatestRelease() + console.log("previous", previous) + const notes = await buildNotes(previous, "dev") await $`gh release edit v${Script.version} --draft=false --title "v${Script.version}" --notes ${notes.join("\n") || "No notable changes"}` } From 08f11f4da6f16dd02024fba0914e13af7ba784c4 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 30 Jan 2026 00:39:49 -0500 Subject: [PATCH 24/33] ci --- script/changelog-debug.ts | 85 --------------------------------------- 1 file changed, 85 deletions(-) delete mode 100644 script/changelog-debug.ts diff --git a/script/changelog-debug.ts b/script/changelog-debug.ts deleted file mode 100644 index 1144d3b0cbc..00000000000 --- a/script/changelog-debug.ts +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env bun - -import { $ } from "bun" -import { parseArgs } from "util" -import { getLatestRelease } from "./changelog" - -const paths = [ - "packages/opencode", - "packages/sdk", - "packages/plugin", - "packages/desktop", - "packages/app", - "sdks/vscode", - "packages/extensions", - "github", -] - -const clean = (text: string) => text.split("\n").filter(Boolean) - -const ref = (value: string, head = false) => { - if (head && value === "HEAD") return value - if (value.startsWith("v")) return value - return `v${value}` -} - -const { values } = parseArgs({ - args: Bun.argv.slice(2), - options: { - from: { type: "string", short: "f" }, - to: { type: "string", short: "t", default: "HEAD" }, - base: { type: "string", short: "b", default: "origin/dev" }, - help: { type: "boolean", short: "h", default: false }, - }, -}) - -if (values.help) { - console.log(` -Usage: bun script/changelog-debug.ts [options] - -Options: - -f, --from Starting version (default: latest GitHub release) - -t, --to Ending ref (default: HEAD) - -b, --base Compare base for ahead/behind (default: origin/dev) - -h, --help Show this help message - -Examples: - bun script/changelog-debug.ts - bun script/changelog-debug.ts -f 1.0.200 -t dev - bun script/changelog-debug.ts -f 1.0.200 -t HEAD -b origin/dev -`) - process.exit(0) -} - -const to = values.to! -const from = values.from ?? (await getLatestRelease()) -const fromRef = ref(from) -const toRef = ref(to, true) - -console.log(`Debugging changelog range: ${fromRef} -> ${toRef}\n`) - -const [ahead, behind] = await $`git rev-list --left-right --count ${values.base}...HEAD` - .text() - .then((text) => text.trim().split("\t")) - -console.log(`Ahead/behind ${values.base}: ahead=${ahead ?? "0"} behind=${behind ?? "0"}`) - -const gh = await $`gh api "/repos/anomalyco/opencode/compare/${fromRef}...${toRef}" --jq '.commits[].sha'` - .text() - .then(clean) - -const localAll = await $`git log ${fromRef}..${toRef} --oneline --format="%H"`.text().then(clean) -const localFiltered = await $`git log ${fromRef}..${toRef} --oneline --format="%H" -- ${paths}`.text().then(clean) - -const ghSet = new Set(gh) -const missing = localFiltered.filter((hash) => !ghSet.has(hash)) - -console.log(`GitHub compare commits: ${gh.length}`) -console.log(`Local commits (all): ${localAll.length}`) -console.log(`Local commits (filtered paths): ${localFiltered.length}`) -console.log(`Filtered commits missing from GitHub compare: ${missing.length}`) - -if (missing.length > 0) { - console.log("\nMissing hashes (first 10):") - console.log(missing.slice(0, 10).join("\n")) -} From 5bef8e316ad99e899afc6caf9f5a3056e68efa04 Mon Sep 17 00:00:00 2001 From: Dax Raad Date: Fri, 30 Jan 2026 00:43:36 -0500 Subject: [PATCH 25/33] ci --- script/changelog.ts | 30 +++++++++++++++++++++++------- script/publish.ts | 6 ------ script/version.ts | 7 ++++++- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/script/changelog.ts b/script/changelog.ts index ace579ee4b6..0c8d65ba18e 100755 --- a/script/changelog.ts +++ b/script/changelog.ts @@ -18,13 +18,29 @@ export const team = [ "R44VC0RP", ] -export async function getLatestRelease() { - return fetch("https://api.github.com/repos/anomalyco/opencode/releases/latest") - .then((res) => { - if (!res.ok) throw new Error(res.statusText) - return res.json() - }) - .then((data: any) => data.tag_name.replace(/^v/, "")) +type Release = { + tag_name: string + draft: boolean + prerelease: boolean +} + +export async function getLatestRelease(skip?: string) { + const data = await fetch("https://api.github.com/repos/anomalyco/opencode/releases?per_page=100").then((res) => { + if (!res.ok) throw new Error(res.statusText) + return res.json() + }) + + const releases = data as Release[] + const target = skip?.replace(/^v/, "") + + for (const release of releases) { + if (release.draft) continue + const tag = release.tag_name.replace(/^v/, "") + if (target && tag === target) continue + return tag + } + + throw new Error("No releases found") } type Commit = { diff --git a/script/publish.ts b/script/publish.ts index 5fa84825b39..84abe6a66a3 100755 --- a/script/publish.ts +++ b/script/publish.ts @@ -2,7 +2,6 @@ import { $ } from "bun" import { Script } from "@opencode-ai/script" -import { buildNotes, getLatestRelease } from "./changelog" const highlightsTemplate = `