diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b6da34dbc..163b8a01a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -41,18 +41,6 @@ jobs: - uses: ./.github/actions/setup-bun - - name: Setup SSH for AUR - if: inputs.bump || inputs.version - run: | - sudo apt-get update - sudo apt-get install -y pacman-package-manager - mkdir -p ~/.ssh - echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - git config --global user.email "opencode@sst.dev" - git config --global user.name "opencode" - ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts || true - - name: Install OpenCode if: inputs.bump || inputs.version run: bun i -g opencode-ai@1.0.169 @@ -77,7 +65,7 @@ jobs: - name: Publish id: publish - run: ./script/publish.ts + run: ./script/publish-start.ts env: OPENCODE_BUMP: ${{ inputs.bump }} OPENCODE_VERSION: ${{ inputs.version }} @@ -212,6 +200,23 @@ jobs: fetch-depth: 0 ref: ${{ needs.publish.outputs.tagName }} - - run: gh release edit ${{ needs.publish.outputs.tagName }} --draft=false + - uses: ./.github/actions/setup-bun + + - name: Setup SSH for AUR + run: | + sudo apt-get update + sudo apt-get install -y pacman-package-manager + mkdir -p ~/.ssh + echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + git config --global user.email "opencode@sst.dev" + git config --global user.name "opencode" + ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts || true + + - run: ./script/publish-complete.ts env: - GH_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }} + OPENCODE_BUMP: ${{ inputs.bump }} + OPENCODE_VERSION: ${{ inputs.version }} + AUR_KEY: ${{ secrets.AUR_KEY }} + GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }} + OPENCODE_RELEASE_TAG: ${{ needs.publish.outputs.tagName }} diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts index a85fde9e2..84b8c1d67 100755 --- a/packages/opencode/script/build.ts +++ b/packages/opencode/script/build.ts @@ -14,72 +14,11 @@ process.chdir(dir) import pkg from "../package.json" import { Script } from "@opencode-ai/script" +import { collectBinaries, collectTargets } from "./utils" -const singleFlag = process.argv.includes("--single") const skipInstall = process.argv.includes("--skip-install") -const allTargets: { - os: string - arch: "arm64" | "x64" - abi?: "musl" - avx2?: false -}[] = [ - { - os: "linux", - arch: "arm64", - }, - { - os: "linux", - arch: "x64", - }, - { - os: "linux", - arch: "x64", - avx2: false, - }, - { - os: "linux", - arch: "arm64", - abi: "musl", - }, - { - os: "linux", - arch: "x64", - abi: "musl", - }, - { - os: "linux", - arch: "x64", - abi: "musl", - avx2: false, - }, - { - os: "darwin", - arch: "arm64", - }, - { - os: "darwin", - arch: "x64", - }, - { - os: "darwin", - arch: "x64", - avx2: false, - }, - { - os: "win32", - arch: "x64", - }, - { - os: "win32", - arch: "x64", - avx2: false, - }, -] - -const targets = singleFlag - ? allTargets.filter((item) => item.os === process.platform && item.arch === process.arch) - : allTargets +const targets = collectTargets() await $`rm -rf dist` @@ -88,17 +27,7 @@ if (!skipInstall) { await $`bun install --os="*" --cpu="*" @opentui/core@${pkg.dependencies["@opentui/core"]}` await $`bun install --os="*" --cpu="*" @parcel/watcher@${pkg.dependencies["@parcel/watcher"]}` } -for (const item of targets) { - const name = [ - pkg.name, - // changing to win32 flags npm for some reason - item.os === "win32" ? "windows" : item.os, - item.arch, - item.avx2 === false ? "baseline" : undefined, - item.abi === undefined ? undefined : item.abi, - ] - .filter(Boolean) - .join("-") +for (const { item, name } of collectBinaries(targets)) { console.log(`building ${name}`) await $`mkdir -p dist/${name}/bin` diff --git a/packages/opencode/script/publish-registries.ts b/packages/opencode/script/publish-registries.ts new file mode 100644 index 000000000..6e9d0e498 --- /dev/null +++ b/packages/opencode/script/publish-registries.ts @@ -0,0 +1,196 @@ +#!/usr/bin/env bun +import { $ } from "bun" +import { Script } from "@opencode-ai/script" +import { collectBinaries, collectTargets } from "./utils" + +if (!Script.preview) { + for (const key of Object.keys(collectBinaries(collectTargets()))) { + if (key.includes("linux")) { + await $`cd dist/${key}/bin && tar -czf ../../${key}.tar.gz *` + } else { + await $`cd dist/${key}/bin && zip -r ../../${key}.zip *` + } + } + + // 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/sst/opencode'", + "arch=('aarch64' 'x86_64')", + "license=('MIT')", + "provides=('opencode')", + "conflicts=('opencode')", + "depends=('ripgrep')", + "", + `source_aarch64=("\${pkgname}_\${pkgver}_aarch64.tar.gz::https://github.com/sst/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/sst/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/sst/opencode'", + "arch=('aarch64' 'x86_64')", + "license=('MIT')", + "provides=('opencode')", + "conflicts=('opencode-bin')", + "depends=('ripgrep')", + "makedepends=('git' 'bun-bin' 'go')", + "", + `source=("opencode-\${pkgver}.tar.gz::https://github.com/sst/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/sst/opencode"`, + ` version "${Script.version.split("-")[0]}"`, + "", + ` depends_on "ripgrep"`, + "", + " on_macos do", + " if Hardware::CPU.intel?", + ` url "https://github.com/sst/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/sst/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/sst/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/sst/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 72632992f..9b93caf11 100755 --- a/packages/opencode/script/publish.ts +++ b/packages/opencode/script/publish.ts @@ -53,196 +53,6 @@ for (const tag of tags) { } if (!Script.preview) { - for (const key of Object.keys(binaries)) { - if (key.includes("linux")) { - await $`cd dist/${key}/bin && tar -czf ../../${key}.tar.gz *` - } else { - await $`cd dist/${key}/bin && zip -r ../../${key}.zip *` - } - } - - // 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/sst/opencode'", - "arch=('aarch64' 'x86_64')", - "license=('MIT')", - "provides=('opencode')", - "conflicts=('opencode')", - "depends=('ripgrep')", - "", - `source_aarch64=("\${pkgname}_\${pkgver}_aarch64.tar.gz::https://github.com/sst/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/sst/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/sst/opencode'", - "arch=('aarch64' 'x86_64')", - "license=('MIT')", - "provides=('opencode')", - "conflicts=('opencode-bin')", - "depends=('ripgrep')", - "makedepends=('git' 'bun-bin' 'go')", - "", - `source=("opencode-\${pkgver}.tar.gz::https://github.com/sst/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/sst/opencode"`, - ` version "${Script.version.split("-")[0]}"`, - "", - ` depends_on "ripgrep"`, - "", - " on_macos do", - " if Hardware::CPU.intel?", - ` url "https://github.com/sst/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/sst/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/sst/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/sst/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` - const image = "ghcr.io/sst/opencode" const platforms = "linux/amd64,linux/arm64" const tags = [`${image}:${Script.version}`, `${image}:latest`] diff --git a/packages/opencode/script/utils.ts b/packages/opencode/script/utils.ts new file mode 100644 index 000000000..d855eba69 --- /dev/null +++ b/packages/opencode/script/utils.ts @@ -0,0 +1,87 @@ +import pkg from "../package.json" + +type Target = { + os: string + arch: "arm64" | "x64" + abi?: "musl" + avx2?: false +} + +const allTargets: Target[] = [ + { + os: "linux", + arch: "arm64", + }, + { + os: "linux", + arch: "x64", + }, + { + os: "linux", + arch: "x64", + avx2: false, + }, + { + os: "linux", + arch: "arm64", + abi: "musl", + }, + { + os: "linux", + arch: "x64", + abi: "musl", + }, + { + os: "linux", + arch: "x64", + abi: "musl", + avx2: false, + }, + { + os: "darwin", + arch: "arm64", + }, + { + os: "darwin", + arch: "x64", + }, + { + os: "darwin", + arch: "x64", + avx2: false, + }, + { + os: "win32", + arch: "x64", + }, + { + os: "win32", + arch: "x64", + avx2: false, + }, +] + +const singleFlag = process.argv.includes("--single") + +export function collectTargets() { + return singleFlag + ? allTargets.filter((item) => item.os === process.platform && item.arch === process.arch) + : allTargets +} + +export function collectBinaries(targets: Target[]) { + return targets.map((item) => { + const name = [ + pkg.name, + // changing to win32 flags npm for some reason + item.os === "win32" ? "windows" : item.os, + item.arch, + item.avx2 === false ? "baseline" : undefined, + item.abi === undefined ? undefined : item.abi, + ] + .filter(Boolean) + .join("-") + + return { name, item } + }) +} diff --git a/script/publish-complete.ts b/script/publish-complete.ts new file mode 100644 index 000000000..a0bc0f323 --- /dev/null +++ b/script/publish-complete.ts @@ -0,0 +1,9 @@ +#!/usr/bin/env bun + +import { $ } from "bun" + +await $`bun install` + +await import(`../packages/opencode/script/publish-registries.ts`) + +await $`gh release edit ${process.env.OPENCODE_RELEASE_TAG} --draft=false` diff --git a/script/publish.ts b/script/publish-start.ts similarity index 100% rename from script/publish.ts rename to script/publish-start.ts index d11b435b4..b8ace899d 100755 --- a/script/publish.ts +++ b/script/publish-start.ts @@ -54,9 +54,6 @@ await import(`../packages/sdk/js/script/publish.ts`) console.log("\n=== plugin ===\n") await import(`../packages/plugin/script/publish.ts`) -const dir = new URL("..", import.meta.url).pathname -process.chdir(dir) - if (!Script.preview) { await $`git commit -am "release: v${Script.version}"` await $`git tag v${Script.version}` @@ -70,3 +67,6 @@ if (!Script.preview) { await Bun.write(process.env.GITHUB_OUTPUT, `releaseId=${release.id}\ntagName=${release.tagName}\n`) } } + +const dir = new URL("..", import.meta.url).pathname +process.chdir(dir)