Skip to content

Commit bdbe334

Browse files
quickbeardclaude
andcommitted
feat: publish to private registry under @quickbeard scope
Main package @quickbeard/codev-code (bin codev-code) plus scoped platform binary packages; a single npmrc scope mapping routes install and optionalDependencies to the private registry. dist/ dir names stay pkg.name-derived (bun compile target derives from them). Docker/AUR/ Homebrew publishing is upstream-only and now exits early. New manual publish-codev workflow drives build + publish with explicit versioning. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 19fc549 commit bdbe334

6 files changed

Lines changed: 112 additions & 17 deletions

File tree

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
name: publish-codev
2+
3+
# Publishes @quickbeard/codev-code (+ platform binary packages) to the private
4+
# npm registry. Manual-only: versioning is explicit, no auto-bumps.
5+
#
6+
# Required repo secrets:
7+
# CODEV_NPM_REGISTRY e.g. https://npm.internal.example.com/
8+
# CODEV_NPM_TOKEN auth token for that registry
9+
on:
10+
workflow_dispatch:
11+
inputs:
12+
version:
13+
description: "Version to publish (e.g. 1.0.0)"
14+
required: true
15+
channel:
16+
description: "npm dist-tag"
17+
default: "latest"
18+
required: false
19+
20+
concurrency: publish-codev
21+
22+
jobs:
23+
publish:
24+
runs-on: ubuntu-latest
25+
timeout-minutes: 60
26+
steps:
27+
- uses: actions/checkout@v4
28+
29+
- uses: oven-sh/setup-bun@v2
30+
with:
31+
bun-version: latest
32+
33+
- name: Install dependencies
34+
run: bun install
35+
36+
- name: Configure npm for the private registry
37+
env:
38+
REGISTRY: ${{ secrets.CODEV_NPM_REGISTRY }}
39+
TOKEN: ${{ secrets.CODEV_NPM_TOKEN }}
40+
run: |
41+
if [ -z "$REGISTRY" ] || [ -z "$TOKEN" ]; then
42+
echo "::error::CODEV_NPM_REGISTRY and CODEV_NPM_TOKEN secrets must be set."
43+
exit 1
44+
fi
45+
# Route the @quickbeard scope to the private registry and attach the
46+
# auth token to that host (npmrc's //host/path:_authToken form).
47+
hostpath="${REGISTRY#*://}"
48+
hostpath="${hostpath%/}"
49+
{
50+
echo "@quickbeard:registry=${REGISTRY%/}/"
51+
echo "//${hostpath}/:_authToken=${TOKEN}"
52+
} >> ~/.npmrc
53+
54+
- name: Build all platform binaries
55+
working-directory: packages/opencode
56+
env:
57+
OPENCODE_VERSION: ${{ inputs.version }}
58+
OPENCODE_CHANNEL: ${{ inputs.channel }}
59+
run: bun run script/build.ts
60+
61+
- name: Publish to private registry
62+
working-directory: packages/opencode
63+
env:
64+
OPENCODE_VERSION: ${{ inputs.version }}
65+
OPENCODE_CHANNEL: ${{ inputs.channel }}
66+
run: bun run script/publish.ts

‎DIVERGENCES.md‎

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,25 @@ future `packages/codev-*`) never conflict and are not tracked here.
1515
| `packages/opencode/src/index.ts` | `scriptName("opencode")` → `"codev code"`; `show()` prefix check `"opencode "` → `"codev code "` | Help/usage text shows the real user-facing command. The `show()` check must always match the scriptName. | Keep our two strings; take upstream's surrounding changes. |
1616
| `packages/opencode/package.json` | bin key `opencode` → `codev-code` (shim path `./bin/opencode` unchanged) | Installed binary is named `codev-code`. | Keep our bin key; take upstream's other fields. |
1717
| `bun.lock` | Mirrors the bin key rename above | Lockfile records workspace bin mappings. | Regenerate with `bun install`; never hand-merge. |
18+
| `packages/opencode/script/build.ts` | Platform packages' `package.json` name scoped to `@quickbeard/codev-code-<platform>` (dist dir names unchanged — the bun compile target derives from them) | Private-registry publishing. | Keep our `name:` line; take upstream's surrounding changes. |
19+
| `packages/opencode/script/publish.ts` | `PUBLISH_NAME = "@quickbeard/codev-code"` const; scoped-name→dir map; main pkg name + bin key; `process.exit(0)` before the Docker/AUR/Homebrew section | npm-only publishing to the private registry; upstream's other channels target anomalyco infra. | Keep our names + early exit; take upstream's npm-section changes above the exit. |
20+
| `packages/opencode/bin/opencode` | `base` prefix `opencode-` → `@quickbeard/codev-code-` | Runtime platform-binary lookup must match the published names. | Keep our prefix. |
21+
| `packages/opencode/script/postinstall.mjs` | `base` prefix `opencode-` → `@quickbeard/codev-code-` | Install-time platform-binary fetch must match the published names. | Keep our prefix. |
1822
| `README.md` | Replaced with fork README | Fork identity, sync + divergence policy. | Keep ours entirely. |
1923
| `README.*.md` (21 translations) | Deleted | Upstream marketing translations, irrelevant to the fork. | Keep deleted (`git rm` the upstream side of modify/delete conflicts). |
2024
| `screenshot-uk.png` | Deleted | Orphaned README-translation asset; referenced by nothing. | Keep deleted. |
2125

2226
## Known rebrand gaps (deliberate, revisit before external distribution)
2327

24-
- **Auto-update**: upstream's updater installs from upstream's release channel. A distributed
25-
`codev-code` binary must not update itself into stock opencode. Ship with
26-
`OPENCODE_DISABLE_AUTOUPDATE=1` (e.g. set by the `codev` wrapper) or patch the installation
27-
module before building release binaries.
28-
- **Build/publish pipeline** (`packages/opencode/script/build.ts`, publish workflows): still
29-
produces `opencode`-named artifacts and publishes to upstream's npm names. Rework when we
30-
set up internal distribution.
28+
- **Auto-update**: upstream's updater (`packages/core/src/installation/`) still hardcodes
29+
`opencode-ai` and upstream's release channels. The codev hub launches the agent with
30+
`OPENCODE_DISABLE_AUTOUPDATE=1`, so the notify/auto path is off in normal use — but a
31+
direct `codev-code upgrade` would still install stock opencode. Patch or disable the
32+
upgrade command before distributing outside the team.
33+
- **Publishing**: npm packages go to the private registry via `.github/workflows/publish-codev.yml`
34+
(manual dispatch; requires `CODEV_NPM_REGISTRY` + `CODEV_NPM_TOKEN` secrets and an explicit
35+
version). Docker/AUR/Homebrew/GitHub-release channels are upstream-only and disabled.
36+
The binary inside platform packages is still named `opencode` (cosmetic, keeps diffs small).
3137
- **UI branding**: TUI logo (`UI.logo()`), desktop/web app names and assets — all still
3238
upstream-branded. Cosmetic; not worth the merge-conflict surface until the product ships
3339
beyond the team.

‎packages/opencode/bin/opencode‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ let arch = archMap[os.arch()]
7070
if (!arch) {
7171
arch = os.arch()
7272
}
73-
const base = "opencode-" + platform + "-" + arch
73+
// Fork: platform binary packages are published under the private @quickbeard
74+
// scope (see script/publish.ts). The scope becomes a directory segment in the
75+
// node_modules lookups below.
76+
const base = "@quickbeard/codev-code-" + platform + "-" + arch
7477
const binary = platform === "windows" ? "opencode.exe" : "opencode"
7578

7679
function supportsAvx2() {

‎packages/opencode/script/build.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,10 @@ for (const item of targets) {
215215
await Bun.file(`dist/${name}/package.json`).write(
216216
JSON.stringify(
217217
{
218-
name,
218+
// Published under the private @quickbeard scope. The dist DIRECTORY
219+
// keeps the pkg.name-derived name — the bun compile target string is
220+
// derived from it above (name.replace(pkg.name, "bun")).
221+
name: name.replace(pkg.name, "@quickbeard/codev-code"),
219222
version: Script.version,
220223
preferUnplugged: true,
221224
os: [item.os],

‎packages/opencode/script/postinstall.mjs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ const archMap = {
2424

2525
const platform = platformMap[os.platform()] ?? os.platform()
2626
const arch = archMap[os.arch()] ?? os.arch()
27-
const base = `opencode-${platform}-${arch}`
27+
// Fork: platform binary packages live under the private @quickbeard scope
28+
// (must match bin/opencode and script/publish.ts).
29+
const base = `@quickbeard/codev-code-${platform}-${arch}`
2830
const sourceBinary = platform === "windows" ? "opencode.exe" : "opencode"
2931
const targetBinary = path.join(__dirname, "bin", "opencode.exe")
3032

‎packages/opencode/script/publish.ts‎

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ import { fileURLToPath } from "url"
77
const dir = fileURLToPath(new URL("..", import.meta.url))
88
process.chdir(dir)
99

10+
// Fork: published names live under the private @quickbeard scope so a single
11+
// npmrc scope mapping (`@quickbeard:registry=<url>`) routes install AND the
12+
// platform-binary optionalDependencies to the private registry. dist/
13+
// directory names stay pkg.name-derived (build.ts derives the bun compile
14+
// target from them); only package.json `name` fields are scoped.
15+
const PUBLISH_NAME = "@quickbeard/codev-code"
16+
1017
async function published(name: string, version: string) {
1118
return (await $`npm view ${name}@${version} version`.nothrow()).exitCode === 0
1219
}
@@ -24,9 +31,12 @@ async function publish(dir: string, name: string, version: string) {
2431
}
2532

2633
const binaries: Record<string, string> = {}
34+
// Scoped published name -> unscoped dist directory (they differ in this fork).
35+
const dirs: Record<string, string> = {}
2736
for (const filepath of new Bun.Glob("*/package.json").scanSync({ cwd: "./dist" })) {
2837
const pkg = await Bun.file(`./dist/${filepath}`).json()
2938
binaries[pkg.name] = pkg.version
39+
dirs[pkg.name] = filepath.replace("/package.json", "")
3040
}
3141
console.log("binaries", binaries)
3242
const version = Object.values(binaries)[0]
@@ -37,15 +47,15 @@ await $`cp ./script/postinstall.mjs ./dist/${pkg.name}/postinstall.mjs`
3747
await Bun.file(`./dist/${pkg.name}/LICENSE`).write(await Bun.file("../../LICENSE").text())
3848
await Bun.file(`./dist/${pkg.name}/bin/${pkg.name}.exe`).write(
3949
[
40-
`echo "Error: ${pkg.name}-ai's postinstall script was not run." >&2`,
50+
`echo "Error: ${PUBLISH_NAME}'s postinstall script was not run." >&2`,
4151
'echo "" >&2',
4252
'echo "This occurs when using --ignore-scripts during installation, or when using a" >&2',
4353
'echo "package manager like pnpm that does not run postinstall scripts by default." >&2',
4454
'echo "" >&2',
4555
'echo "To fix this, run the postinstall script manually:" >&2',
46-
`echo " cd node_modules/${pkg.name}-ai && node postinstall.mjs" >&2`,
56+
`echo " cd node_modules/${PUBLISH_NAME} && node postinstall.mjs" >&2`,
4757
'echo "" >&2',
48-
`echo "Or reinstall ${pkg.name}-ai without the --ignore-scripts flag." >&2`,
58+
`echo "Or reinstall ${PUBLISH_NAME} without the --ignore-scripts flag." >&2`,
4959
"exit 1",
5060
"",
5161
].join("\n"),
@@ -54,9 +64,9 @@ await Bun.file(`./dist/${pkg.name}/bin/${pkg.name}.exe`).write(
5464
await Bun.file(`./dist/${pkg.name}/package.json`).write(
5565
JSON.stringify(
5666
{
57-
name: pkg.name + "-ai",
67+
name: PUBLISH_NAME,
5868
bin: {
59-
[pkg.name]: `./bin/${pkg.name}.exe`,
69+
"codev-code": `./bin/${pkg.name}.exe`,
6070
},
6171
scripts: {
6272
postinstall: "node ./postinstall.mjs",
@@ -73,10 +83,15 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write(
7383
)
7484

7585
const tasks = Object.entries(binaries).map(async ([name]) => {
76-
await publish(`./dist/${name}`, name, binaries[name])
86+
await publish(`./dist/${dirs[name]}`, name, binaries[name])
7787
})
7888
await Promise.all(tasks)
79-
await publish(`./dist/${pkg.name}`, `${pkg.name}-ai`, version)
89+
await publish(`./dist/${pkg.name}`, PUBLISH_NAME, version)
90+
91+
// Fork: npm packages only. Upstream's Docker/AUR/Homebrew distribution below
92+
// targets anomalyco-owned registries and taps; keep the code for cheap merges
93+
// but don't run it. (Env-guarded so tsc doesn't mark the rest unreachable.)
94+
if (!process.env.CODEV_PUBLISH_UPSTREAM_CHANNELS) process.exit(0)
8095

8196
const image = "ghcr.io/anomalyco/opencode"
8297
const platforms = "linux/amd64,linux/arm64"

0 commit comments

Comments
 (0)