Skip to content

feat(forge): support CNB (cnb.cool) as a forge provider - #1066

Open
abigmiu wants to merge 1 commit into
spacering-net:mainfrom
abigmiu:feat/cnb-forge
Open

abigmiu wants to merge 1 commit into
spacering-net:mainfrom
abigmiu:feat/cnb-forge

Conversation

@abigmiu

@abigmiu abigmiu commented Oct 30, 2026

Copy link
Copy Markdown

What

Adds CNB (cnb.cool) as a fourth forge provider, alongside GitHub, GitLab and Gitea — in the repository panel, the delivery path, auto-processing, and Settings → Version Control.

CNB is not a GitHub/Gitea dialect, and the differences produce silently wrong answers rather than 404s, so it gets its own client (src-tauri/src/forge/cnb.rs):

  • the REST base is a separate host (api.cnb.cool), not the web origin;
  • auth is Authorization: Bearer <token>;
  • every id is a string on the wire, including an issue's number;
  • issues and merge requests are separate collections (/-/issues, /-/pulls);
  • a merged merge request is state: "closed" + is_merged: true — the flag, not the state, says "landed";
  • 403 means a missing token scope, and errmsg names it.

Behaviour worth reviewing

  • Issue bodies are read on demand. CNB's issue list omits body (its pull list carries one), so forge_issue_detail fetches the item when the panel opens — one request, only for the item being read. Every other forge ships the body on the row and never takes this path (the command errors loudly if one ever does).
  • Search is hidden on CNB's merge-request tab. keyword exists on issues only; on pulls it is ignored, so the box would be a control whose input is dropped. A query left over from the other tab is cleared rather than silently kept.
  • No least_recently_updated order for CNB. Its order_by has no ascending-updated spelling.
  • No diff counters / commit count / check strip for CNB — its pull payload carries none, and saying so beats zeroes. mergeable_state: "checking" maps to "unknown", not "mergeable".
  • Merge is guarded by the head SHA (CNB's merge endpoint takes none), and returns Ok(None) after landing — the merge succeeded even if the read-back fails.

Tests

  • Rust unit tests in forge/cnb.rs pin the wire-shape traps: string numbers, absent issue body, refs/heads/ stripping, the merged flag beating closed, the per-collection spelling of "all", and file paths recovered from URLs (filename is only a base name).
  • An #[ignore]d live smoke test (CNB_TEST_TOKEN=… cargo test … forge::cnb::tests::live -- --ignored) for wire-shape drift.
  • Frontend: useIssueBody draws nothing until the read lands (rather than flashing "No description"), and a row that arrives with a body never pays for a second read.

Checks

  • pnpm lint . ✅
  • cargo check --features test-utils ✅
  • pnpm test — 2 pre-existing failures unrelated to this change (task-schedule-dialog is date-dependent and fails at HEAD; activity-rows is a timing flake)

CNB is Tencent's code hosting platform. Its API is not a GitHub or Gitea
dialect: the REST base is a separate host (api.cnb.cool), auth is a Bearer
token, ids are strings on the wire, and issues/merge requests are separate
collections. A GitHub fallback would answer every request with an HTML page
and a 200, so it gets its own provider variant and client.

Backend:
- forge/cnb.rs: list/detail/comments/labels/state/create for issues and
  merge requests, changed-file list, merge, and the delivery path
  (find/create/get pull), plus the scan used by auto-processing
- ForgeProvider::Cnb wired through mod.rs, auth.rs (host detection,
  api_base_for, web_origin), auto, deliver and every forge_* command
- validate_cnb_token: GET /user against api.cnb.cool, with 403 read as a
  missing scope rather than a bad token
- forge_issue_detail: CNB's issue LIST omits the body, so the panel reads
  the item itself when it opens (CNB only; other forges ship it on the row)

Frontend:
- CNB accounts section, add-account dialog, token validation
- merge-request wording for CNB everywhere GitLab already had it
- forge page: hide the search box on CNB's PR tab (no keyword parameter),
  drop least-recently-updated from its sort options (no ascending updated
  order), and reset a filter that the current forge cannot apply
- useIssueBody: fetch an issue's description on demand, drawing nothing
  until the answer lands rather than flashing "No description"
- i18n for all 10 locales
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant