dsh-plugin-forge-cnb: Integrate CNB Merge Requests for the DSH Forge Panel

Introduction

The DSH forge registry defaults to regular code hosting services. If a team is hosted on CNB (cnb.cool), the status, checks, diff, review, and merge information for change requests (merge requests) cannot be read by the panel because it doesn’t have access to the corresponding data source. Consequently, the change request and working tree panels will not function.

genius-alray/dsh-plugin-forge-cnb does exactly this: it registers a ForgeProvider to ctx.forge, allowing the DSH forge panel to directly read CNB merge requests. The plugin is categorized as “Network Tools”, maintained by genius-alray, has 1 GitHub star, version 0.1.0 in package.json, and the license is MIT.

Below is an introduction to its positioning, capabilities, and how to install and enable it.

What is it

One-sentence positioning: A DSH bundle distributed on GitHub, providing a CNB (cnb.cool) provider for the DSH forge registry.

It registers the provider via ctx.forge, overriding the reading and operation of merge requests: status, additions/deletions, mergeability, checks, reviewers, diff, file contents, review submission, and merging.

There are no third-party dependencies at runtime; it only uses the global fetch and Node.js built-in modules, requiring the host Node.js >= 22. Neither the client nor the DSH package itself requires changes—registering the provider is sufficient.

The code repository provides both an English README.md and a Chinese README.zh.md.

Core Capabilities

Comparing with the contract method table in the README, the capabilities are as follows:

Contract Method CNB Source
repositoryFromRemote / changeRequestFromUrl / changeRequestUrl Parse remote URL and MR URL, supporting https, ssh://, scp-style remote, nested groups (a/b/c), .git suffix, ports; the two URL functions are strict inverse functions of each other
fetch GET /{repo}/-/pulls/{n} + GET /{repo}/-/pulls/{n}/commit-statuses, plus additions/deletions statistics for each head commit (.../files), head commit date (.../git/commits/{sha}), and cached repository merge settings
fetchChecks Same reading as fetch, but always returns the full list of checks, with no 50-item polling limit
findByHead GET /{repo}/-/pulls?state=open&page_size=100&page=N, client-side filtering (CNB has no interface to filter by head branch)
diff GET /{repo}/-/pulls/{n}/files, assembled into a Git-style unified patch
fileText / fileUrl GET /{repo}/-/git/raw/{commit}/{path} (falls back to base64 contents interface when HTTP 413), and the web address /-/blob/{commit}/{path}
merge Re-reads the MR, compares head.sha with the confirmed commit, then calls PUT /{repo}/-/pulls/{n}/merge with merge_style; never sends force
markReady CNB has no corresponding interface, returns explicit failure and prompts the user to edit the WIP title in the Web UI
submitReview POST /{repo}/-/pulls/{n}/reviews, with event and inline comments
readiness GET /user; token expired or missing is read as unauthenticated, prompting the user to execute cnb login

Notable Mapping Limitations

Some of CNB’s data models are inconsistent with what the panel expects. The plugin handles these according to fixed rules; you should be aware of these differences before using them:

  • updatedAt: CNB’s MR details do not have a timestamp. The plugin uses the committer date of the head commit (GET /{repo}/-/git/commits/{headSha}, cached by commit); if this read fails, it takes the time of the first observation of that head commit.
  • closedAt: Always null. CNB’s MR interface does not have closed_at / merged_at.
  • unresolvedThreads: Always 0. CNB’s review comments do not have resolved / unresolved markers.
  • additions/deletions: Are the total of changed files in the MR. CNB truncates this list, so the total for very large MRs may be low; there is no MR-level statistics interface.
  • mergeability: conflict / no-merge-base (or blocked_on is code_conflict / no_merge_base) maps to conflicting; mergeable / merging / merged maps to mergeable; the rest map to unknown.
  • mergeReady: Requires state == open && !is_wip && !is_merged && mergeable_state == mergeable && blocked_on == unblocked. Just looking at mergeable_state is not enough—a mergeable MR may still be waiting for a review or CI.
  • reviewDecision: Takes the value if any reviewer gives changes_requested, approved if all pass, otherwise review-required; none if there are no reviewers.
  • mergeMethods: Comes from GET /{repo}/-/settings/pull-request, cached by repository for 5 minutes. If you lack permission to read (e.g., token missing repo-manage:r), a default collection [merge, squash, rebase] is provided to prevent the merge button from disappearing.
  • checksRollup: Directly uses the aggregated state of CNB’s commit-statuses; individual checks use created_at / updated_at as the start and end time. The polling snapshot limit is 50 items and marked checksTruncated, fetchChecks returns the full list.

Diff Assembly Method

This detail determines whether the panel can correctly display changes. The panel uses parsePatch from jsdiff 9.0.0 to parse diff(), which parses paths based on diff --git / --- / +++ headers and silently discards paragraphs that only have hunks but no file headers. CNB’s /pulls/{n}/files only returns hunks, and the filename field contains only the filename itself. Therefore, the plugin:

  1. Restores the repository’s relative full path from blob_url / raw_url / contents_url (.../-/blob/{sha}/{path}), never using filename;
  2. For each file, outputs diff --git a/<path> b/<path>, new file mode 100644 / deleted file mode 100644, --- a/<path> + +++ b/<path> (missing side uses /dev/null), then concatenates the recorded hunks as-is, with LF line endings, and a trailing newline;
  3. Empty patches (add / delete / modify) are rendered as Binary files ... differ, which jsdiff records as isBinary, rather than being treated as empty changes;
  4. When the payload exposes previous_filename, it preserves the rename source.

Additionally, review bodies are submitted via POST /pulls/{n}/reviews with event + inline comments; empty review bodies are omitted rather than sending "". Inline comments use the repository’s full path, the same as diff().

Installation and Enablement

The plugin is a DSH bundle, distributed via GitHub rather than npm. The installation steps given in the README are in two parts: first, retrieve the repository, then install the extracted directory as a bundle to the profile (you can use the Plugin Manager’s install-bundle flow, or the plugin_manager tool with action: install_bundle):

git clone https://github.com/genius-alray/dsh-plugin-forge-cnb

The bundle comes with cordis.patch.yml, responsible for inserting the provider line; during installation, select the bundle, no need to manually pick lines. The host needs Node.js >= 22, and a valid cnb login session (~/.cnb/token, the plugin re-reads after CLI refresh).

Enabling only requires one record in the profile:

- insert:
    - id: forge-cnb
      name: 'dsh-plugin-forge-cnb'
      config:
        hosts: ['cnb.cool']
        tokenFile: '~/.cnb/token'
        timeoutMs: 10000
        maxPages: 3

Configuration field description:

Configuration Item Default Value Meaning
hosts ['cnb.cool'] The web host of the provider service; repositories on other hosts are ignored
tokenFile ~/.cnb/token CNB CLI credential file; CNB_TOKEN in the environment can override it
timeoutMs 10000 The deadline for a single HTTP request, stacked with the caller’s cancellation mechanism
maxPages 3 The number of open MR pages read by findByHead each time, 100 items per page

The plugin only writes data to the repository when explicitly performing a merge or submitting a review; all other operations are read-only.

Typical Usage

There are two paths for binding MRs to sessions, neither requiring manual configuration of repository information.

The first is automatic discovery. The forge session layer executes git in the session checkout, first using repositoryFromRemote to map the remote to a specific repository, then calling findByHead(repository, branch) to find the open MR that the head points to the current branch.

The second is pasting the MR address. Paste the CNB MR URL directly into the session, and changeRequestFromUrl will bind it immediately. The binding relationship is re-parsed via changeRequestUrl -> changeRequestFromUrl, which is why these two URL functions are strict inverse functions.

It is worth noting that gh command output parsing is not applicable here; that is specific to GitHub-CLI. CNB only has git remote / branch discovery and pasting MR URL as two binding paths.

Regarding credentials, the provider reads ~/.cnb/token (written by the official cnb CLI), extracts .auths["cnb.cool"].access_token, and the token is not printed or logged. The CNB token expires in about 8 hours, and the plugin’s handling is:

  1. Re-read when the file’s mtime or size changes; cnb login refresh doesn’t require a restart;
  2. 401 or errcode: 16 responses are reported as unauthenticated, prompting the user to execute cnb login;
  3. CNB_TOKEN can provide a token for a single process, replacing the file.

Used scopes: repo-pr:r (MR, status, files), repo-code:r (commit, raw files), repo-manage:r (merge settings), repo-pr:rw (merge), repo-notes:rw (review). Missing scopes appear as HTTP 403 (errcode 10023), reported as unavailable / failed, and will not crash the panel.

Applicable Scenarios and Notes

Suitable for teams or individuals hosted on cnb.cool who want to directly view CNB MR status, checks, diff, and perform merges in the DSH forge panel.

A few reminders:

  • The plugin runs with the permissions of the current dsh process and can read files and credentials that the host can read. Before installing, you should check the source code and MIT license yourself to confirm the source is trustworthy.
  • It modifies the profile via a bundle; installation and enablement require a restart or profile reload to take effect. It is recommended to back up the current configuration before making changes.
  • With 1 star and version 0.1.0, it indicates that both the maintainer and user base are very small. If you encounter problems, it is better to look at the source code or issues rather than waiting for a fix.
  • The CNB data model limitations listed above (closedAt, unresolvedThreads, truncation of additions/deletions, etc.) are determined by the upstream interface and will not change in the short term; don’t treat panel values as authoritative.

Conclusion

The value of this plugin lies in filling the gap in DSH forge panel support for CNB: no need to modify the client, and after registering the provider, you can read/write CNB merge requests, checks, diffs, reviews, and merges. It is small in code size, has zero runtime dependencies, has very detailed mapping rules, is suitable as a reference implementation to read, and is also suitable to use directly.

Directory page: https://www.skillhub.cn/plugins/genius-alray/dsh-plugin-forge-cnb

Source code: https://github.com/genius-alray/dsh-plugin-forge-cnb

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye