From 481ac6fd47af9e4eb772ae6415f5e3aca46cb86e Mon Sep 17 00:00:00 2001 From: minhn4 Date: Wed, 15 Jul 2026 08:25:22 +0700 Subject: [PATCH] chore: rebrand platform binary to codev and add release workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Close the "Publishing" rebrand gap: platform packages now ship bin/codev instead of bin/opencode (build outfile, publish stub, and postinstall copy all renamed), and a fork-only release workflow publishes to npm from CI and creates the v GitHub release with codev- binaries for the latest channel — making the install docs' Releases link and the updater's GitHub fallback real. Co-Authored-By: Claude Fable 5 --- .github/workflows/release.yml | 77 ++++++++++++++++++++++++ DIVERGENCES.md | 14 +---- README.md | 4 ++ packages/opencode/script/build.ts | 4 +- packages/opencode/script/postinstall.mjs | 12 ++-- packages/opencode/script/publish.ts | 4 +- 6 files changed, 94 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..507499b5b2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,77 @@ +# Fork-only workflow: publishes codev-code (+ platform packages) to npm and, +# for the latest channel, creates the v GitHub release with the +# platform binaries attached (the install docs and the updater's GitHub +# fallback in src/installation/index.ts point at these releases). +# +# Requires the NPM_TOKEN repository secret: a granular npm automation token +# with read/write access to the codev-code packages (bypasses 2FA prompts). +# +# The version comes from packages/opencode/package.json — bump it first for a +# fork-only re-release (see README → Publishing); script/release.ts refuses to +# republish an existing version. +name: release + +on: + workflow_dispatch: + inputs: + channel: + description: "npm dist-tag to publish under" + type: string + required: false + default: "latest" + +# Never cancel a publish mid-flight; queue instead. +concurrency: + group: release + cancel-in-progress: false + +permissions: + contents: write + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + release: + runs-on: ubuntu-latest + timeout-minutes: 90 + steps: + - name: Checkout repository + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + + - name: Setup Bun + uses: ./.github/actions/setup-bun + + - name: Authenticate npm + run: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Build and publish to npm + working-directory: packages/opencode + run: bun run release "${{ inputs.channel }}" + + # dist/ holds one opencode-/ directory per target (the dist + # names stay pkg.name-derived — see script/build.ts) plus the main + # opencode/ package dir; only the platform dirs carry real binaries. + - name: Package release binaries + if: inputs.channel == 'latest' + working-directory: packages/opencode/dist + run: | + for dir in opencode-*/bin; do + key="${dir%/bin}" + artifact="codev-${key#opencode-}" + case "$key" in + *linux*) (cd "$dir" && tar -czf "../../$artifact.tar.gz" *) ;; + *) (cd "$dir" && zip -r "../../$artifact.zip" *) ;; + esac + done + + - name: Create GitHub release + if: inputs.channel == 'latest' + env: + GH_TOKEN: ${{ github.token }} + run: | + VERSION=$(jq -r .version packages/opencode/package.json) + gh release view "v$VERSION" >/dev/null 2>&1 || gh release create "v$VERSION" --title "v$VERSION" --generate-notes + gh release upload "v$VERSION" packages/opencode/dist/*.tar.gz packages/opencode/dist/*.zip --clobber diff --git a/DIVERGENCES.md b/DIVERGENCES.md index 594ef997f7..3a9ce8c663 100644 --- a/DIVERGENCES.md +++ b/DIVERGENCES.md @@ -72,10 +72,10 @@ from the squash message first, or use a plain merge. | `packages/tui/src/ui/dialog.tsx` | Added a `persistent` store flag (with `setPersistent`/`persistent` accessors): escape and the click-outside backdrop no-op while set, and ctrl+c dispatches `app.exit.confirm` (base-mode-only, so suppressed under modal) instead of closing; every `replace`/`clear` resets it to false | The CoDev gateway configure flow is a non-dismissable first-run gate; ctrl+c stays the quit escape hatch (double-press — see the double-Ctrl+C row). Default-false keeps every other dialog dismissable. | Keep our persistent flag, the escape/backdrop guards, the ctrl+c `app.exit.confirm` dispatch, and the reset lines; take upstream's surrounding changes. | | `packages/tui/src/ui/dialog-select.tsx` | Added a `persistent?` prop that hides the top-right `esc` hint | A non-dismissable select shouldn't advertise `esc` (the dialog store's persistent flag blocks the actual close). | Keep our prop + the `Show` wrapper around the esc label; take upstream's surrounding changes. | | `packages/tui/src/ui/dialog-prompt.tsx` | Added `persistent?` (hides the `esc` hint + `DialogPrompt.show` re-arms the store flag), `onInput?` (fires on textarea content change), and `ref?` props | The configure flow's prompts (SSO login, manual credentials) are non-dismissable, and the SSO view watches `onInput` for the lone-`c` copy shortcut on the paste-back link (needs the `ref` to clear the field). | Keep our three props, the `Show` around the esc label, the textarea `onContentChange`, and the `.show` re-arm; take upstream's surrounding changes. | -| `packages/opencode/script/build.ts` | Platform packages' `package.json` name `codev-code-` (dist dir names unchanged — the bun compile target derives from them) | npm publishing under the fork's names. | Keep our `name:` line; take upstream's surrounding changes. | -| `packages/opencode/script/publish.ts` | `PUBLISH_NAME = "codev-code"` const; npm description "CoDev Code — the AI agent harness"; published-name→dir map; main pkg name + bin key `codev`; embedded README (hub via `codevhub install`, direct install runs `codev`); env-guarded exit before the Docker/AUR/Homebrew section | npm-only publishing under the fork's names (unscoped, public on npmjs); the installed command is `codev`; upstream's other channels target anomalyco infra. | Keep our names, description, bin key + early exit; take upstream's npm-section changes above the exit. | +| `packages/opencode/script/build.ts` | Platform packages' `package.json` name `codev-code-` (dist dir names unchanged — the bun compile target derives from them); compiled binary emitted as `bin/codev` (was `bin/opencode`; smoke-test path follows) | npm publishing under the fork's names; the binary inside platform packages carries the fork's name (postinstall.mjs copies it into the `codev` bin shim). The `Script.release` upload block stays upstream-shaped and unused — the fork's `.github/workflows/release.yml` packages and uploads GitHub-release artifacts itself. | Keep our `name:` line and `bin/codev` paths; take upstream's surrounding changes. | +| `packages/opencode/script/publish.ts` | `PUBLISH_NAME = "codev-code"` const; npm description "CoDev Code — the AI agent harness"; published-name→dir map; main pkg name + bin `codev` → `./bin/codev.exe` (postinstall stub file renamed from `${pkg.name}.exe`, matching the renamed platform binary); embedded README (hub via `codevhub install`, direct install runs `codev`); env-guarded exit before the Docker/AUR/Homebrew section | npm-only publishing under the fork's names (unscoped, public on npmjs); the installed command is `codev`; upstream's other channels target anomalyco infra. | Keep our names, description, bin mapping + early exit; take upstream's npm-section changes above the exit. | | `packages/opencode/bin/opencode` | `base` prefix `opencode-` → `codev-code-` | Runtime platform-binary lookup must match the published names. | Keep our prefix. | -| `packages/opencode/script/postinstall.mjs` | `base` prefix `opencode-` → `codev-code-` | Install-time platform-binary fetch must match the published names. | Keep our prefix. | +| `packages/opencode/script/postinstall.mjs` | `base` prefix `opencode-` → `codev-code-`; source/target binary names `opencode`/`opencode.exe` → `codev`/`codev.exe` (matches build.ts's `bin/codev` and publish.ts's `bin/codev.exe` stub); temp-dir prefix and failure message rebranded | Install-time platform-binary fetch must match the published package and binary names. | Keep our prefix and binary names. | | `packages/tui/src/logo.ts` | Pixel glyphs "open"+"code" → "co"+"dev"; added composed `wordmark` (plain lines) and `ansiWordmark` (bold brand teal + drop shadow) exports for CLI banners | Branded wordmark art; `ansiWordmark` feeds the CLI help banner and session epilogue. | Keep our glyphs + exports; take upstream's surrounding changes. | | `packages/tui/src/component/logo.tsx` | Renders the lowercase codev wordmark in the theme's `secondary` color (bold, with a `tint`-derived drop shadow) instead of upstream's two-tone "open"+"code" | Home banner shows the brand wordmark in the active theme's brand color (teal in dark, purple in light). | Keep our version wholesale. | | `packages/tui/src/theme/assets/codev.json` (new file), `packages/tui/src/theme/index.ts`, `packages/tui/src/context/theme.tsx` | Added the `codev` theme — upstream's `opencode.json` palette with `darkSecondary` → `#0891b2` (brand teal) — registered it in the theme index, and made it the default/fallback theme (`"opencode"` → `"codev"` in the store default, kv fallback, and load-failure fallbacks). `theme/assets/opencode.json` itself matches upstream again. | Brand accent lives in a fork-owned theme instead of edits to upstream's palette; the default theme carries the brand. | Keep our theme file, its index registration, and the fallback strings; when upstream changes the opencode palette, mirror it into `codev.json` (keeping our `darkSecondary`). | @@ -143,14 +143,6 @@ from the squash message first, or use a plain merge. ## Known rebrand gaps -- **Publishing**: npm packages publish as `codev-code` / `codev-code-` — unscoped, - therefore PUBLIC on npmjs (like `codev-ai`). Local-only for now (no CI workflow): run - `bun run release [channel] [--skip-build]` from `packages/opencode` with npm publish - auth in place. The version comes from package.json (upstream-synced — releases track - the upstream version the fork was built from; numeric revision like - `1.17.16-1` for fork-only re-releases, see README → Publishing). - Docker/AUR/Homebrew/GitHub-release channels are upstream-only and - disabled. The binary inside platform packages is still named `opencode` (cosmetic). - **UI branding**: TUI banner, default-theme accent, desktop/web app names, and favicon/PWA assets are rebranded (see table). Still upstream-branded, deliberately: in-app i18n strings mentioning "OpenCode" (now only in the `en`/`es` locale files — see the locale-trim rows above; diff --git a/README.md b/README.md index 68a753d92e..530b6226af 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,10 @@ Notes: - The wrapper refuses to run when the version is already on npm — for a fork-only re-release, bump the revision suffix first (see [Bumping the version](#bumping-the-version) above). - Verify after publishing: `npm view codev-code` and `npm i -g codev-code && codev --version`. +### Publishing from CI + +The `release` workflow (GitHub → Actions → release → Run workflow) runs the same `bun run release [channel]` on a GitHub runner, and for the `latest` channel it also creates the `v` GitHub release with the platform binaries attached (`codev--.tar.gz`/`.zip`) — the install docs and the updater's GitHub fallback point at those releases. It needs an `NPM_TOKEN` repository secret: a granular npm automation token with read/write access to the `codev-code` packages. + ## Syncing and divergences The upstream sync procedure and the divergence policy live in [DIVERGENCES.md](DIVERGENCES.md). diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts index 01c9240b3c..7f1e0c215f 100755 --- a/packages/opencode/script/build.ts +++ b/packages/opencode/script/build.ts @@ -180,7 +180,7 @@ for (const item of targets) { autoloadTsconfig: true, autoloadPackageJson: true, target: name.replace(pkg.name, "bun") as any, - outfile: `dist/${name}/bin/opencode`, + outfile: `dist/${name}/bin/codev`, execArgv: [`--user-agent=opencode/${Script.version}`, "--use-system-ca", "--"], windows: {}, }, @@ -200,7 +200,7 @@ for (const item of targets) { // Smoke test: only run if binary is for current platform if (item.os === process.platform && item.arch === process.arch && !item.abi) { - const binaryPath = `dist/${name}/bin/opencode` + const binaryPath = `dist/${name}/bin/codev` console.log(`Running smoke test: ${binaryPath} --version`) try { const versionOutput = await $`${binaryPath} --version`.text() diff --git a/packages/opencode/script/postinstall.mjs b/packages/opencode/script/postinstall.mjs index d04b21499a..ede10f0618 100644 --- a/packages/opencode/script/postinstall.mjs +++ b/packages/opencode/script/postinstall.mjs @@ -24,11 +24,11 @@ const archMap = { const platform = platformMap[os.platform()] ?? os.platform() const arch = archMap[os.arch()] ?? os.arch() -// Fork: platform binary packages are published as codev-code- -// (must match bin/opencode and script/publish.ts). +// Fork: platform binary packages are published as codev-code-, +// each shipping bin/codev (must match script/build.ts and script/publish.ts). const base = `codev-code-${platform}-${arch}` -const sourceBinary = platform === "windows" ? "opencode.exe" : "opencode" -const targetBinary = path.join(__dirname, "bin", "opencode.exe") +const sourceBinary = platform === "windows" ? "codev.exe" : "codev" +const targetBinary = path.join(__dirname, "bin", "codev.exe") function supportsAvx2() { if (arch !== "x64") return false @@ -129,7 +129,7 @@ function installPackage(name) { const version = packageJson.optionalDependencies?.[name] if (!version) return - const temp = fs.mkdtempSync(path.join(os.tmpdir(), "opencode-install-")) + const temp = fs.mkdtempSync(path.join(os.tmpdir(), "codev-install-")) try { const result = childProcess.spawnSync( "npm", @@ -177,7 +177,7 @@ function main() { } throw new Error( - `It seems your package manager failed to install the right opencode CLI package. Try manually installing ${packageNames() + `It seems your package manager failed to install the right codev CLI package. Try manually installing ${packageNames() .map((name) => JSON.stringify(name)) .join(" or ")}.`, ) diff --git a/packages/opencode/script/publish.ts b/packages/opencode/script/publish.ts index c641b87ca1..5940b7526d 100755 --- a/packages/opencode/script/publish.ts +++ b/packages/opencode/script/publish.ts @@ -65,7 +65,7 @@ await Bun.file(`./dist/${pkg.name}/README.md`).write( "", ].join("\n"), ) -await Bun.file(`./dist/${pkg.name}/bin/${pkg.name}.exe`).write( +await Bun.file(`./dist/${pkg.name}/bin/codev.exe`).write( [ `echo "Error: ${PUBLISH_NAME}'s postinstall script was not run." >&2`, 'echo "" >&2', @@ -91,7 +91,7 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write( url: "git+https://github.com/quickbeard/codev-code.git", }, bin: { - codev: `./bin/${pkg.name}.exe`, + codev: "./bin/codev.exe", }, scripts: { postinstall: "node ./postinstall.mjs",