diff --git a/CHANGELOG.md b/CHANGELOG.md index 11bcbd2295..1db652f6f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,19 @@ file is the human-readable summary of what changed and why. carried a redundant copy of themselves: a second, nested copy of the same tarball, packed in by accident whenever a release was retried. Removing it takes each platform package from ~112MB back to ~55MB. +- **The desktop app installs to a folder named after CoDev** — on Windows it + installed itself into `%LOCALAPPDATA%\Programs\@opencode-aidesktop`, and kept + its downloaded updates in `@opencode-aidesktop-updater` beside it. Both names + came from an internal package id that was never meant to be read, by way of a + packaging rule that uses it rather than the product name for a per-user + install. They are now `codev` and `codev-updater` (`codev-beta`/`codev-dev` on + those channels, which previously shared one folder with each other and with + the release build). Already-installed copies stay where they are and keep + updating — the installer reuses the location it recorded — so this changes + where a fresh install lands, not where an existing one lives; a machine that + had downloaded an update leaves the old cache folder behind, which is safe to + delete. Linux and macOS are unaffected: the `.deb` package name is already + `codev`, and nothing on macOS is derived from this name. ### Updates diff --git a/DIVERGENCES.md b/DIVERGENCES.md index 654785f70f..b73b896fcd 100644 --- a/DIVERGENCES.md +++ b/DIVERGENCES.md @@ -188,7 +188,7 @@ from the squash message first, or use a plain merge. | `packages/app/src/i18n/{en,es}.ts`, `packages/app/src/pages/layout/deep-links.ts` | App-self-reference "OpenCode" → "CoDev" across the shared web UI's i18n dicts (28 strings per locale): settings row descriptions, the update toasts, the WSL onboarding step/status strings, the `dialog.server.description`, `error.chain.mcpFailed`, `sidebar.gettingStarted.line1`, the provider-connect "use {{provider}} models in OpenCode" lines, and `app.name.desktop`. The Zen free-models dialog title (`dialog.model.unpaid.freeModels.title`, "Free models provided by OpenCode") was **neutralized** to "Free models included" / "Modelos gratuitos incluidos" rather than rebranded or kept — it read oddly branded either way. i18n **keys** are untouched (they carry `Opencode`/lowercase `opencode`, which never match the exact-case `OpenCode` in values). The deep-link bridge window type `OpenCodeWindow` → `CodevWindow` (its `__CODEV__` global was already renamed in the desktop row). | These render in the desktop app (and the web console), which the fork ships; closes the "in-app i18n strings" gap for this package. | Keep our strings; take upstream's surrounding changes. PRESERVED deliberately: the real external-service names **"OpenCode Zen"** / **"OpenCode Go"** (in the `opencodeZen.*` provider-connect keys and the "OpenCode Go" upsell), plus every `opencode.ai/*` URL — the fork routes to those actual services, so the display text must match what users land on. The built-in **"OpenCode" theme** name (`packages/ui/src/theme/context.tsx`, the shiki theme id, `desktop-theme.schema.json`) is also kept — it names a specific palette, and the fork ships its own `codev` theme alongside it. | | Browser-opened docs-site links: `packages/tui/src/app.tsx`, `packages/app/src/desktop-menu.ts`, `packages/app/src/components/{settings-general,settings-v2/general,dialog-custom-provider}.tsx`, `packages/app/src/pages/{home,layout,error}.tsx`, `packages/app/src/i18n/{en,es}.ts`, `packages/opencode/src/cli/cmd/{providers,github.handler}.ts` | Links the app opens in the user's browser repointed from `opencode.ai` to the fork's hosted docs. Each one derives its base from `DOCS_URL` (`packages/codev-brand/src/index.ts`, base64-encoded) rather than hardcoding the host: the site is locale-routed and the Code docs live under `/code`, and no URL takes a trailing slash (the site 308-redirects `…/themes/` to `…/themes`). Sites: TUI "Open docs" command and app Documentation menu item → docs root; settings theme "Learn more" (v1 + v2) → `…/code/themes`; custom-provider dialog link → `…/code/providers#custom-provider`; CLI Cloudflare-gateway hint → `…/code/providers#cloudflare-ai-gateway`; GitHub-agent hint → `…/code/github`; home/layout sidebar Help buttons `opencode.ai/desktop-feedback` → docs root. The error page's report button `opencode.ai/desktop-feedback` → `github.com/quickbeard/codev-code/issues` instead (it's a report action, not a docs lookup), icon `discord` → `github`, and the en/es `error.page.report.*` strings → "…CoDev team" / "on GitHub". Help submenu (rendered by the shipped web UI's Windows app menu too): "Support Forum" (upstream Discord invite) deleted — the fork has no forum; "Share Feedback"/"Report a Bug" → `github.com/quickbeard/codev-code/issues/new?template=feature-request.yml`/`bug-report.yml` (the fork's template names are hyphenated, unlike upstream's underscored ones). | Fork users should land on the fork's docs and report issues to the fork, not upstream's site/Discord. NOT repointed: Zen/Go product URLs (`opencode.ai/zen`, `/go`, `/auth`), `$schema`/theme-schema ids, API/share/changelog endpoints, MCP OAuth `client_uri`, prompt-file mentions. Also NOT on this base: the ripgrep mirror (`/codev/docs/code/downloads/…`, see its own row) — it is a `public/` static asset, not locale-routed content, and 404s under `/en/docs/code`. | Keep our URLs, icon, and strings; take upstream's surrounding changes. Repoint any new browser-opened `opencode.ai` docs/help link to the fork docs base; leave the NOT-repointed list. | | `packages/desktop/src/main/index.ts`, `packages/desktop/src/main/windows.ts` | APP_NAMES + window title → "CoDev" (Dev/Beta variants too) | Desktop app display name ("Code" dropped once the app carried a Work section beside Code). | Keep our names. | -| `packages/desktop/electron-builder.config.ts` | `productName` ×3 → "CoDev…"; artifactName → `codev-code-desktop-…` (deliberately kept when the product name dropped "Code" — the update feed's `latest*.yml` and the hand-upload flow reference these artifact names, and nothing user-visible shows them); protocol display name. Auto-update `publish` repointed off GitHub entirely: upstream's beta `anomalyco/opencode-beta` and prod `anomalyco/opencode` collapse to one shared `generic` config (`const publish`) pointing at the fork's MinIO bucket — `url` from `process.env.CODEV_UPDATE_URL` falling back to a base64 `UPDATE_URL` const, deliberately **unversioned** — it is the only URL an installed build ever reads (`app-update.yml` is baked at package time and nothing calls `setFeedURL`), so a version in it would leave that build able to see only its own release folder. The manifests sit at that base and the artifacts they name live under `/`, which the fork-only `scripts/stage-release.ts` arranges by prefixing each `url`/`path` in `latest*.yml`; electron-updater resolves them with `new URL(name, base)` (same `atob` convention as `packages/codev-brand/src/index.ts`), plus two fields that are load-bearing rather than cosmetic: `channel: "latest"` and `useMultipleRangeRequest: false`. Also a second `extraResources` entry shipping `../opencode/dist/node` → `server-bundle` (filter `node.js` + `*.wasm`, dropping the sourcemap, which outweighs the bundle) so the `@codev/sandbox` providers have a real on-disk server directory to mount into guests; `src/main/sandbox/bundle.ts` (fork-only) resolves it under `process.resourcesPath` by that name. Also the sandbox vz helper: `mac.extraResources` ships `packages/codev-sandbox/native/vzhelper/dist/codev-vmm` (built universal by `scripts/prebuild.ts`) as `Resources/codev-vmm`, and an `afterSign` hook (`signVmmHelper`) re-signs it with `vzhelper.entitlements` (`com.apple.security.virtualization` — an entitlement the Electron app itself must not carry) under the same identity that signed the app, hardened runtime on, timestamp only for non-ad-hoc, before electron-builder's built-in notarization runs. | Installer/product branding, and a packaged build must not offer upstream OpenCode releases as CoDev updates. GitHub could never have worked here: `quickbeard/codev-code` is private and `GitHubProvider.getLatestTagName` requests `/releases/latest` unauthenticated, and the repo has never carried a desktop asset. A bucket also splits nothing — one channel, one prefix. **`channel: "latest"` is required, not a default restated**: `detectUpdateChannel` is on by default, so with it absent `getResolvedPublishConfig` takes the channel from `AppInfo.channel`, which is the version's first semver prerelease component — `8` for `1.18.4-8` — and `getUpdateInfoFileName` then writes `8.yml`/`8-mac.yml`/`8-linux.yml` while the app requests `latest.yml` (`autoUpdater.channel` in `src/main/updater.ts`), i.e. a permanent `ERR_UPDATER_CHANNEL_FILE_NOT_FOUND`. **`useMultipleRangeRequest: false` is required for MinIO**, which answers a multipart range request with the whole object; electron-updater hardcodes the same `false` for its own `s3` provider citing minio/minio#5285 but defaults it to `true` for any `generic` URL that is not `s3.amazonaws.com`. Beta keeps a `publish` block despite the fork shipping no beta installers because `UPDATER_ENABLED` covers that channel and a build with no publish config gets no `app-update.yml` at all, so every check would fail with ENOENT. `${channel}` is deliberately NOT used as a URL macro — `macroExpander` expands it to that same `AppInfo.channel` (`8`), not `CODEV_CHANNEL`. appId is `ai.codev.desktop{,.beta,.dev}` and `rpm.packageName` is `codev`/`codev-beta`/`codev-dev` — see the desktop identity row for the full rename and what it costs. | Keep our names and the whole `publish` const; take upstream's other config. Never drop `channel` or `useMultipleRangeRequest` to "match upstream" — both are covered by `electron-builder.config.test.ts`. If upstream reworks its publish targets, re-point ours rather than merging theirs. Note `generic` has no publisher (`createPublisher` returns null), so installers reach the bucket by hand; build with `--publish never`. Keep the `server-bundle` extraResource and the `codev-vmm` extraResource + `afterSign` hook (all covered by `electron-builder.config.test.ts`) — renaming either strands packaged sandbox providers, and dropping `afterSign` ships a helper without the virtualization entitlement, which fails at VM creation (or fails notarization if left ad-hoc signed). | +| `packages/desktop/electron-builder.config.ts` | `productName` ×3 → "CoDev…"; artifactName → `codev-code-desktop-…` (deliberately kept when the product name dropped "Code" — the update feed's `latest*.yml` and the hand-upload flow reference these artifact names, and nothing user-visible shows them); protocol display name. Auto-update `publish` repointed off GitHub entirely: upstream's beta `anomalyco/opencode-beta` and prod `anomalyco/opencode` collapse to one shared `generic` config (`const publish`) pointing at the fork's MinIO bucket — `url` from `process.env.CODEV_UPDATE_URL` falling back to a base64 `UPDATE_URL` const, deliberately **unversioned** — it is the only URL an installed build ever reads (`app-update.yml` is baked at package time and nothing calls `setFeedURL`), so a version in it would leave that build able to see only its own release folder. The manifests sit at that base and the artifacts they name live under `/`, which the fork-only `scripts/stage-release.ts` arranges by prefixing each `url`/`path` in `latest*.yml`; electron-updater resolves them with `new URL(name, base)` (same `atob` convention as `packages/codev-brand/src/index.ts`), plus two fields that are load-bearing rather than cosmetic: `channel: "latest"` and `useMultipleRangeRequest: false`. Also a second `extraResources` entry shipping `../opencode/dist/node` → `server-bundle` (filter `node.js` + `*.wasm`, dropping the sourcemap, which outweighs the bundle) so the `@codev/sandbox` providers have a real on-disk server directory to mount into guests; `src/main/sandbox/bundle.ts` (fork-only) resolves it under `process.resourcesPath` by that name. Also the sandbox vz helper: `mac.extraResources` ships `packages/codev-sandbox/native/vzhelper/dist/codev-vmm` (built universal by `scripts/prebuild.ts`) as `Resources/codev-vmm`, and an `afterSign` hook (`signVmmHelper`) re-signs it with `vzhelper.entitlements` (`com.apple.security.virtualization` — an entitlement the Electron app itself must not carry) under the same identity that signed the app, hardened runtime on, timestamp only for non-ad-hoc, before electron-builder's built-in notarization runs. | Installer/product branding, and a packaged build must not offer upstream OpenCode releases as CoDev updates. GitHub could never have worked here: `quickbeard/codev-code` is private and `GitHubProvider.getLatestTagName` requests `/releases/latest` unauthenticated, and the repo has never carried a desktop asset. A bucket also splits nothing — one channel, one prefix. **`channel: "latest"` is required, not a default restated**: `detectUpdateChannel` is on by default, so with it absent `getResolvedPublishConfig` takes the channel from `AppInfo.channel`, which is the version's first semver prerelease component — `8` for `1.18.4-8` — and `getUpdateInfoFileName` then writes `8.yml`/`8-mac.yml`/`8-linux.yml` while the app requests `latest.yml` (`autoUpdater.channel` in `src/main/updater.ts`), i.e. a permanent `ERR_UPDATER_CHANNEL_FILE_NOT_FOUND`. **`useMultipleRangeRequest: false` is required for MinIO**, which answers a multipart range request with the whole object; electron-updater hardcodes the same `false` for its own `s3` provider citing minio/minio#5285 but defaults it to `true` for any `generic` URL that is not `s3.amazonaws.com`. Beta keeps a `publish` block despite the fork shipping no beta installers because `UPDATER_ENABLED` covers that channel and a build with no publish config gets no `app-update.yml` at all, so every check would fail with ENOENT. `${channel}` is deliberately NOT used as a URL macro — `macroExpander` expands it to that same `AppInfo.channel` (`8`), not `CODEV_CHANNEL`. appId is `ai.codev.desktop{,.beta,.dev}` and `rpm.packageName` is `codev`/`codev-beta`/`codev-dev` — see the desktop identity row for the full rename and what it costs. Also `extraMetadata.name`, single-sourced with `rpm.packageName` as `PACKAGE_NAMES`: package.json `name` stays `@opencode-ai/desktop` per the internal-names rule, but electron-builder does not treat it as internal — `appInfo.sanitizedName` strips the slash to `@opencode-aidesktop` and that reaches users as the per-user Windows install directory (`%LOCALAPPDATA%\Programs\@opencode-aidesktop`; a one-click `perMachine: false` install takes `sanitizedName`, **not** `productFilename` — see `getWindowsInstallationDirName`), as the updater cache directory (`-updater`, baked into `app-update.yml` on every platform), and as the source of the Debian package name when `deb.packageName` is unset. The `deb` name is the one place this changes nothing user-visible: `FpmTarget` passes `appInfo.linuxPackageName` (`sanitizedProductName`, i.e. `CoDev`/`CoDev Dev`, since the package id starts with `@`) straight to `fpm`, which normalizes it itself — it downcases with a warning and turns the space into a hyphen, so the built packages were already `codev`/`codev-beta`/`codev-dev`. Passing a clean name just silences that warning. Do not "fix" the `deb` name by citing this row. It has to be per channel for the same reason appId is: one shared value installs every channel over the same directory and shares one updater cache. | Keep our names and the whole `publish` const; take upstream's other config. Never drop `channel` or `useMultipleRangeRequest` to "match upstream" — both are covered by `electron-builder.config.test.ts`. If upstream reworks its publish targets, re-point ours rather than merging theirs. Note `generic` has no publisher (`createPublisher` returns null), so installers reach the bucket by hand; build with `--publish never`. Keep the `server-bundle` extraResource and the `codev-vmm` extraResource + `afterSign` hook (all covered by `electron-builder.config.test.ts`) — renaming either strands packaged sandbox providers, and dropping `afterSign` ships a helper without the virtualization entitlement, which fails at VM creation (or fails notarization if left ad-hoc signed). Keep `extraMetadata.name` and do not "simplify" it away as a restatement of `rpm.packageName` — dropping it silently reinstates `@opencode-aidesktop` as the install directory, which no build log or test mentions unless you look inside a packaged app's `app-update.yml`; `electron-builder.config.test.ts` covers it per channel. | | `packages/app/src/utils/refcount.ts` (+ `.test.ts`) | `createRefCountMap` creates each item in its own root owned by the scope that created the map (disposed when the last pinning caller releases it), and callers without a reactive owner borrow the item without pinning it. | Upstream ran `create` in the *first caller's* scope, so a shared item's internal cleanups (e.g. the dir SDK context's event unsubscribe in `server-sdk.tsx`) fired when that first view closed while other holders still used the item — live updates for the directory silently died. Ownerless callers (event handlers, async continuations: `notification.tsx` session lookup, `layout.tsx` openSession) also registered cleanups Solid warns can never run and pinned items forever. | Keep ours; take upstream's surrounding changes. The two added tests ("ownerless caller borrows without pinning", "item's internal cleanups survive its first caller") pin the semantics. | | `packages/app/src/app.tsx`, `packages/app/src/utils/session-route.ts` | `session-route.ts` gains `serverKeyFromRoute`, a non-throwing variant of `requireServerKey` (which is now defined in terms of it and kept for callers that want the throw). `app.tsx`'s `TargetServerRoute` and `LegacyTargetSessionRoute` read the route's server key through it: `TargetServerRoute` holds it in a memo, passes `undefined` rather than a bogus connection to the SDK/sync providers, and falls back to ``; `LegacyTargetSessionRoute` wraps its `SessionRouteErrorBoundary` in a `Show` on the same memo. | `requireServerKey` throws on a segment that is missing or does not round-trip, and both call sites ran it during render — so the throw escaped from a memo and killed the entire renderer with the fatal error screen. The param does go missing in normal use: removing a server navigates away (`tabs.removeServer` → `navigate("/")`) and the route's params clear before its subtree unmounts, which is how deleting a sandbox with an open session took the window down. Worse, the desktop restores the last active route on launch, so the crash repeated on every relaunch until the persisted URL changed. Rendering nothing was not enough on its own — a `/server/…` route that cannot mount its providers leaves the shell half-built and fails further down in the command registry — hence the redirect home. This is the same class as the `project-avatar-state.ts` guard below and is an upstream bug. | Keep the guard; take upstream's other changes to these files. If upstream makes the route resilient its own way, drop ours in favour of theirs. Do not "simplify" the route back to calling `requireServerKey` directly — that is the crash. | | `packages/app/src/context/server-sdk.tsx` | Event-stream failures log only after 3 consecutive failed attempts (`logStreamIssue`), with the error normalized to `name: message`; any received event resets the count. | The first failed attempt usually races a server being stopped or restarted (sandbox reset, sidecar relaunch): the stream dies before the renderer learns the server is going away and the context is disposed before a real retry, so logging attempt one produced a spurious `[global-sdk] event stream error` for normal lifecycle. A genuinely unreachable server still logs within about a second (250 ms reconnect delay). | Keep the threshold and shared `logStreamIssue`; take upstream's surrounding stream changes. | @@ -328,7 +328,18 @@ from the squash message first, or use a plain merge. inside `~/.local/share/codev` (`opencode.db` in released builds; `opencode-.db`, e.g. `opencode-local.db`, when running from source — see `core/src/database/database.ts`) keep upstream naming on purpose. Renaming them would be a permanent merge-conflict - machine for names users never see. Do not "clean these up". + machine for names users never see. Do not "clean these up". But check that "users never + see it" is actually true before filing something here — it was not for the desktop + package. electron-builder derives user-visible names from `packages/desktop/package.json` + `name`, so `@opencode-ai/desktop` was on screen as the Windows per-user install directory + (`%LOCALAPPDATA%\Programs\@opencode-aidesktop`) and as the updater cache directory + (`@opencode-aidesktop-updater`). Fixed without renaming the package: `extraMetadata.name` + in `electron-builder.config.ts` overrides what the packager derives, so the internal id and + the user-visible one are now decoupled (see that table row). The desktop app's persisted + store file names (`opencode.settings`, `opencode.{draft,workspace,global,window,updater}.*` + in `%APPDATA%\ai.codev.desktop`) are the same shape of leak and are still upstream-branded + deliberately — unlike a derived name, renaming those orphans existing state, so it needs a + migration rather than a rename (see the desktop-rebrand row). - **VS Code extension**: ships as a locally built `.vsix` (`cd sdks/vscode && bun run vsix`), not a Marketplace listing. Fully rebranded for that path: `codev.*` command IDs + CoDev Code titles, `codev` terminal launching `codev --port`, rebranded README body, and the CLI's IDE diff --git a/packages/desktop/electron-builder.config.test.ts b/packages/desktop/electron-builder.config.test.ts index f01e2be206..1156529fda 100644 --- a/packages/desktop/electron-builder.config.test.ts +++ b/packages/desktop/electron-builder.config.test.ts @@ -28,6 +28,28 @@ for (const channel of channels) { }) } +// electron-builder derives the per-user Windows install directory +// (%LOCALAPPDATA%\Programs\) and the updater cache directory +// (-updater) from package.json `name`, not from productName: a one-click +// perMachine:false install takes appInfo.sanitizedName. Left alone that is +// "@opencode-aidesktop" — the upstream package id, on screen. It also has to be +// per channel, or dev/beta/prod installs land on top of each other. +for (const channel of channels) { + test(`brands the install and updater cache directories for ${channel.channel}`, async () => { + const previous = process.env.CODEV_CHANNEL + process.env.CODEV_CHANNEL = channel.channel + + const module = await import(`./electron-builder.config.ts?metadata=${channel.channel}`) + const config = module.default as Configuration + + if (previous === undefined) delete process.env.CODEV_CHANNEL + else process.env.CODEV_CHANNEL = previous + + expect(config.extraMetadata?.name).toBe(channel.packageName) + expect(config.extraMetadata?.name).not.toInclude("opencode") + }) +} + // The scheme the OS hands back to us on a deep link. It is registered here and // consumed by app.setAsDefaultProtocolClient plus parseUrl in the renderer's // deep-links module: all three have to agree or the link opens nothing. diff --git a/packages/desktop/electron-builder.config.ts b/packages/desktop/electron-builder.config.ts index cc26787eef..52858960d2 100644 --- a/packages/desktop/electron-builder.config.ts +++ b/packages/desktop/electron-builder.config.ts @@ -67,6 +67,15 @@ const APP_IDS = { prod: "ai.codev.desktop", } as const +// The package name electron-builder derives user-visible names from. Per +// channel like appId: one shared value installs every channel over the same +// directory. Matches rpm.packageName, which is the same identity on Linux. +const PACKAGE_NAMES = { + dev: "codev-dev", + beta: "codev-beta", + prod: "codev", +} as const + // Base64 (like packages/codev-brand/src/index.ts) to keep the host out of // casual greps, not as a secret — the decoded URL ships in every packaged // build's app-update.yml. @@ -102,12 +111,25 @@ const getBase = (appId: string): Configuration => ({ output: "dist", buildResources: "resources", }, - // Linux launchers are .desktop files, so this is the desktop file name, - // not just the app id. For prod, app id "ai.codev.desktop" becomes - // "ai.codev.desktop.desktop". - // https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html - // https://www.electron.build/docs/linux/ extraMetadata: { + // package.json `name` stays "@opencode-ai/desktop" — internal package ids + // are deliberately not renamed — but electron-builder does not treat it as + // internal. appInfo.sanitizedName strips the slash to "@opencode-aidesktop" + // and that string reaches the user two ways: the per-user Windows install + // directory (%LOCALAPPDATA%\Programs\ — a one-click perMachine:false + // install uses sanitizedName, NOT productFilename, see + // getWindowsInstallationDirName) and the updater cache directory + // ("-updater", baked into app-update.yml on every platform). + // Overriding the name here rebrands both without renaming the workspace + // package. It also feeds the Debian package name when deb.packageName is + // unset, but that one was never wrong: fpm downcases and hyphenates what it + // is given, so those packages already built as codev/codev-beta/codev-dev. + name: PACKAGE_NAMES[channel], + // Linux launchers are .desktop files, so this is the desktop file name, + // not just the app id. For prod, app id "ai.codev.desktop" becomes + // "ai.codev.desktop.desktop". + // https://developer.gnome.org/documentation/guidelines/maintainer/integrating.html + // https://www.electron.build/docs/linux/ desktopName: `${appId}.desktop`, }, files: ["out/**/*", "resources/**/*"], @@ -204,7 +226,7 @@ function getConfig() { ...base, appId, productName: "CoDev Dev", - rpm: { packageName: "codev-dev" }, + rpm: { packageName: PACKAGE_NAMES.dev }, } } case "beta": { @@ -218,7 +240,7 @@ function getConfig() { // all — every check would fail with ENOENT. Sharing prod's feed keeps // the unused channel functional rather than broken. publish, - rpm: { packageName: "codev-beta" }, + rpm: { packageName: PACKAGE_NAMES.beta }, } } case "prod": { @@ -228,7 +250,7 @@ function getConfig() { productName: "CoDev", protocols: { name: "CoDev", schemes: ["codev"] }, publish, - rpm: { packageName: "codev" }, + rpm: { packageName: PACKAGE_NAMES.prod }, } } }