DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

Charlestam218 /

Charlestam218/dsh-github-publish

Verified

DSH 插件发布器:把本地做好的插件一键推送到 GitHub 仓库(凭据 → 建仓 → 提交 → 推送 → 可选 Release)

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@8281be51

dsh-github-publish · 插件发布

把本地做好的 DSH 插件一键上传到 GitHub 仓库:自动初始化仓库、提交改动、按需建仓、推送,可选打标签并发布 Release。整个操作在 DSH 的图形界面里完成,不需要记 git 命令,也不需要 gh CLI。

  • 界面位置:设置 → 插件发布
  • 宿主半区(Node):本地 git 操作 + GitHub REST 调用
  • 浏览器半区(Web):设置分区 UI,只通过本机 HTTP 与宿主通信,永远看不到令牌明文

目录

  • 一分钟上手
  • 安装
  • 界面说明
  • 一键上传做了什么
  • GitHub 凭据
  • 安全设计
  • 设置项
  • 目录结构
  • 自检
  • 故障排查
  • 已知边界

一分钟上手

  1. 安装本插件并重启 DSH(见下一节)。
  2. 打开 设置 → 插件发布。
  3. 在「GitHub 凭据」里粘贴一个 Personal Access Token,点「保存并验证」。令牌会被校验(读一次 /user)后存到本机。
  4. 在「本地插件」里找到你的插件(默认自动发现 profile 中 link: 安装的插件与 node_modules 下的软链插件),点 一键上传。
  5. 右侧/下方出现任务面板:准备 → 初始化仓库 → 提交 → 确认仓库 → 设置远端 → 推送 → 校验。完成后给出仓库链接。

没有令牌也能先点 预检:它只做只读检查(目录、git 状态、仓库是否存在),不写盘、不建仓、不推送。


安装

# 默认装官方桌面端 profile(%USERPROFILE%\.dsh\profiles\desktop)
pwsh -File install.ps1

# 指定 profile
pwsh -File install.ps1 -ProfileDir "D:\some\profile"

# 不用软链,直接拷贝一份(改了源码要重装才生效)
pwsh -File install.ps1 -Copy

脚本做两件事:在 <profile>\node_modules\dsh-github-publish 建一个指向本包目录的 junction,并把包名写进 <profile>\package.json 的 dependencies 与 dsh.profile.bundles。

如果系统禁止运行未签名脚本(about_Execution_Policies 报错),在命令前加一段进程级放行: Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force(只影响当前窗口,不改机器策略)。

装完必须重启 DSH 客户端:profile 未启用 HMR 时,新 bundle 只在启动时装载。

卸载:

pwsh -File uninstall.ps1              # 保留设置与令牌
pwsh -File uninstall.ps1 -PurgeData   # 连设置与令牌一起删

界面说明

区块 作用
GitHub 凭据 显示令牌是否就绪、来源(插件设置 / 环境变量 / 系统凭据管理器)、掩码与账号;可保存、验证、清除。
目标仓库 账号或组织、可见性(公开/私有)、仓库名模板、是否自动建仓、是否写 topics、是否打标签发 Release、是否允许强制覆盖、是否允许回退系统凭据。
发现范围 额外扫描根目录(每个目录向下扫描两层找 package.json)与手动添加的插件目录。
本地插件 每个插件一行:包名/版本、真实路径、git 事实(分支、提交数、是否干净)、GitHub 侧状态(仓库是否存在、是否同步),以及 预检 / 一键上传 按钮。
当前任务 步骤进度、实时日志(同时落盘到 <DSH_HOME>\dsh-github-publish\logs\)、结果链接与取消按钮;推送被拒绝时出现 强制覆盖并重试。

一键上传做了什么

步骤 内容 失败时的行为
prepare 检查 git 可用、目录可读、package.json 合法;位于更大仓库内部时默认拒绝 给出原因与处理建议
git-init 目录不是仓库时 git init -b main;补一份最小 .gitignore(node_modules/ 等) 原样回报 git 输出
commit git add -A,有改动就提交(提交信息模板可配),身份取 git config,缺失时用 GitHub 账号的无回复邮箱 提示补 user.name/user.email
github-repo GET /repos/... 判断仓库是否存在(插件已有指向 GitHub 的 origin 时默认沿用该仓库),不存在且开启自动建仓则创建(用户走 /user/repos,组织走 /orgs/{org}/repos);按需写 topics 令牌/权限问题给出对应提示
remote 把 origin 指向 GitHub 返回的 clone_url(企业版同样正确) —
push 用一次性 HTTP 头携带令牌推送当前分支(-- 终止选项解析,禁用仓库自带钩子),并建立 upstream 被拒绝时提示开启强制覆盖;强制推送用远端实时 sha 作 --force-with-lease 预期值;鉴权失败时提示令牌权限
release 可选:v<版本> 标签 + GitHub Release,已存在则跳过 跳过并记录原因,不影响整体成功
verify 读远端分支头提交与本地 HEAD 比对 不一致 → 任务判失败(提示分支保护 / pushurl 被改写等可能原因);没令牌或分支还不存在 → 步骤标记为「跳过」并给出提示

任何一个步骤失败都会:把该步骤标红、把错误与建议写进任务面板与日志文件,且不回滚已完成的本地工作(重试即可继续)。


GitHub 凭据

解析顺序(第一个命中者生效):

  1. 插件设置里保存的令牌 —— <DSH_HOME>\dsh-github-publish\token(0600)
  2. 环境变量 GITHUB_TOKEN
  3. 环境变量 GH_TOKEN
  4. 系统凭据管理器:git credential fill(Git Credential Manager 已登录过 GitHub 时可直接复用;用 credential.interactive=false + GCM_INTERACTIVE=never 调用,任何情况下都不会弹出登录窗口;本机没有存凭据时会立刻返回“无令牌”,可用设置项关闭这条回退)
  5. 都没有 —— 只能对已存在的仓库尝试推送,建仓一定会失败并提示配置令牌

需要什么权限

  • classic token:勾选 repo(要发 Release / 写 topics 也在这个范围内)
  • fine-grained token:Contents: Read and write;要自动建仓还需 Administration: Read and write,并授权目标账号/组织

令牌只用于两处:GitHub REST 调用(Authorization: Bearer …)与 git push(http.extraheader 一次性注入)。


安全设计

令牌

  • 只在宿主进程内存里流转,接口与界面只返回掩码(ghp_…abcd)与来源;
  • 推送时用环境变量 GIT_CONFIG_COUNT/GIT_CONFIG_KEY_0/GIT_CONFIG_VALUE_0 注入 HTTP 头,不进 argv、不写 .git/config,进程退出即消失;
  • 带令牌的 git 调用(push / ls-remote / tags)与发布期间的每一次 git 调用都指向一个空 hooks 目录(<DSH_HOME>/dsh-github-publish/no-hooks):仓库自带的 pre-push/pre-commit、仓库级 core.hooksPath 都不会执行(命令行 -c 优先级最高,实测压得住仓库本地配置);
  • git 子进程的环境会被剃掉机密:删除 GITHUB_TOKEN/GH_TOKEN 等常见变量名,并删除任何取值等于已登记令牌的变量——仓库里的 filter.*.clean、core.fsmonitor 这类"必然要执行"的程序即使跑了,也拿不到令牌;
  • 所有写进日志/错误信息的文本都过 redact():既抹当前令牌(含其 base64 形式),也按模式抹掉 任何 URL userinfo、ghp_/github_pat_ 形态、Authorization 头与 password= 行——origin 里残留的别的令牌同样不会被打印;
  • 令牌单独存放(token 文件),删文件即撤销,不需要动设置;
  • 自检里有专门断言:.git/config、.git 目录全量文件、发布日志、任务响应里都不允许出现令牌明文。

参数与远端安全

  • branch / owner / repo 全部走白名单校验(lib/validate.js):非法直接 400。git 会解析位置参数之后的选项,不做这层校验时 branch: "--mirror" 会变成真正的 --mirror(删远端引用);
  • 纵深防御:所有 refspec 都放在 -- 之后,git push origin -- <branch>、git fetch/ls-remote ... -- <branch>;
  • 推送前清掉仓库里能"把带令牌的请求引到别处"的配置:remote.origin.pushurl / receivepack / uploadpack / proxy、http.proxy / https.proxy / http.sslVerify、core.gitProxy、url.*.insteadOf / pushInsteadOf(git remote set-url 只改 url,不会动 pushurl);清掉哪些键会写进任务告警,清理失败直接中止推送;同样的中和也内建在 lib/git.js 的 push() 里,直接调用内层 API 同样受保护;
  • 强制推送的预期值来自 git ls-remote 的实时 sha,并按 refs/heads/<branch> 精确比对(ls-remote 的 pattern 是后缀匹配,远端同时有 a/b 与 b 时取错行会让 lease 失真);远端没有该分支时用「期望不存在」;换 origin 时会清掉旧的 refs/remotes/origin/*;
  • GitHub API 只跟随同源重定向;跨主机或降级到 http:// 的重定向直接报错,不把 Bearer 令牌交出去。

发布行为

  • 位于更大仓库内部的插件目录默认拒绝发布(否则 git add -A 会把整个仓库推上去),需要显式勾选「只发布这个子目录(在插件目录内单独 git init)」;
  • 默认不做强制推送;开启后只覆盖「读取到的那个远端快照」(--force-with-lease=<branch>:<sha>);
  • 不删除远端任何内容,不做 push --delete,不 remote prune;
  • git add -A 之后扫描将要提交的文件名,命中 .env、*.pem、id_rsa*、credentials* 等形态时在界面与日志里告警(默认可见性是 public,误推代价很大);
  • 默认 .gitignore 只忽略 node_modules/、*.log 与系统垃圾文件;dist/、build/ 不再被静默排除——发布的是成品,是否包含由你决定;
  • 远端默认分支与本次推送分支不一致时给出提示(否则 GitHub 首页看起来"没更新");
  • 每次发布的完整日志落在 <DSH_HOME>\dsh-github-publish\logs\publish-*.log。

Windows 说明:token/日志目录按 POSIX 权限位创建(0600 / 0700),但 Windows 上 Node 的 mode 不生效,实际保护来自用户目录 ACL。需要更强的保护请使用系统凭据管理器(本插件支持回退)或自行给目录收紧 ACL。

HTTP 接口(/dsh-github-publish/api/*)

  1. 只接受回环地址(127.0.0.1/::1)连接,其余 403;
  2. 必须带 x-dsh-github-publish: 1 自定义头 —— 跨站表单/图片请求无法伪造,跨源 fetch 会先触发预检,而本路由不返回任何 CORS 头;
  3. 带 Origin 时必须与 Host 同源;
  4. 只接受 application/json,请求体上限 256 KB(超出回 413),响应一律 no-store。

设置项

保存在 <DSH_HOME>\dsh-github-publish\settings.json。

键 默认 说明
owner "" 空 = 用令牌所属账号;组织名直接填组织
visibility public public / private
repoTemplate {name} 支持 {name}(去 scope 包名)、{scope}、{full}、{version}
createRepo true 仓库不存在时自动创建
reuseExistingRemote true 插件已有指向 GitHub 的 origin 时,沿用该仓库而不是另建新仓(账号/仓库名被显式指定时以指定值为准)
setTopics true 用 package.json 的 keywords + dsh-plugin/deepseek-harness 写 topics
release false 打 v<version> 标签并发布 Release
allowForce false 允许 --force-with-lease
useCredentialHelper true 无令牌时回退到系统凭据管理器
commitMessage chore(release): {name} v{version} 提交信息模板
extraRoots [] 额外扫描根目录(向下两层)
manualDirs [] 手动添加的插件目录
includeInstalled true 是否扫描 profile 的 node_modules 软链插件
apiBase https://api.github.com GitHub REST 基址(企业版改这里)
remoteBase https://github.com 展示用基址

环境变量覆盖(不会写进磁盘,适合企业部署与自检):

DSH_GITHUB_PUBLISH_API_BASE=http://127.0.0.1:8080   # GitHub API 基址
DSH_GITHUB_PUBLISH_REMOTE_BASE=https://ghe.corp     # 远端基址
DSH_GITHUB_PUBLISH_OWNER=some-org                   # 默认 owner
DSH_GITHUB_PUBLISH_NO_CREDENTIAL_HELPER=1           # 禁止回退系统凭据管理器

目录结构

dsh-github-publish/
  package.json          dsh.bundle.patch + dsh.client 声明
  cordis.patch.yml      插入 profile 配置树的挂载声明
  lib/
    index.js            宿主半区入口:HTTP 路由、请求防护、状态与发现接口
    publish.js          一键发布流水线(job/步骤/日志/失败提示)
    discover.js         插件发现(link: 依赖、node_modules 软链、扫描根、手动目录)
    git.js              git 调用封装(非交互、有界输出、一次性鉴权头)
    github.js           GitHub REST 客户端(零依赖 node:https)
    credentials.js      令牌解析(设置 → 环境变量 → git credential)与脱敏
    validate.js         分支/账号/仓库名白名单校验 + 机密文件名识别
    settings.js         路径推导、设置读写、令牌文件(0600)
    client.js           浏览器半区成品 bundle(设置分区 UI,手写无编译)
  test/
    harness.mjs         宿主半区端到端自检(假 GitHub + 本地裸仓库)
    client-smoke.mjs    浏览器半区冒烟测试(真 React + jsdom)
  install.ps1 / uninstall.ps1

依赖:只用 Node 内置模块(node:http(s)、node:child_process、node:fs…),零运行时依赖;浏览器半区只 require('react')(DSH 的 PLATFORM_MODULES 提供),无需构建步骤。


自检

宿主半区端到端(不需要真实 GitHub,会起一个假 REST 服务 + 本地裸仓库,并断言令牌不落盘):

node test\harness.mjs          # 130 项断言
node test\harness.mjs --keep   # 保留临时目录便于排查

覆盖:环境状态、请求防护(缺头/错误 Content-Type/超大请求体)、无令牌失败路径、令牌保存/校验/清除、插件发现、预检不写盘、首次发布全链路(建仓+提交+推送+校验)、幂等二次发布、沿用既有 origin、远端分叉(拒绝→强制覆盖)、陈旧跟踪引用下的强制推送、嵌套仓库防护、设置持久化、并发 409 与取消、参数注入防护(--mirror/--dry-run/非法 owner 与仓库名)、远端校验不一致判失败、机密文件告警、钩子隔离(A/B 对照)、pushurl/insteadOf/http.proxy 重定向防护、a/b 与 b 同名后缀下的 lease 精确匹配、仓库级 filter.clean 环境脱敏、内层 push() 防护、脱敏与校验函数单测。

浏览器半区冒烟(真 React 18 + jsdom 渲染整个面板并点击按钮):

# 一次性装上测试期依赖(不进插件包)
npm i --prefix ..\..\.testdeps react@18.3.1 react-dom@18.3.1 jsdom@24.1.3
node test\client-smoke.mjs     # 44 项断言

npm 默认缓存目录不可写时(EPERM: ...\_cacache),加 --cache ..\..\.npm-cache 指定一个工作区内的缓存目录。


故障排查

现象 原因 处理
设置里没有「插件发布」 未安装 / 未重启 / 客户端 bundle 未送达 确认 dsh.profile.bundles 含本包,重启 DSH;看客户端控制台有没有 client-modules: client bundle not found
面板提示「读不到宿主接口」 宿主半区未加载(fiber 失败) 看 DSH 日志里 dsh-github-publish 的加载行;确认 lib/index.js 存在
保存令牌返回 401 令牌无效或过期 重新生成(classic 勾 repo;fine-grained 给 Contents 读写)
建仓返回 403 / 422 权限不足 / 仓库名被占用 fine-grained 需要 Administration 读写;换个仓库名或先手动建仓
推送被拒绝(non-fast-forward) 远端有本地没有的提交 先 git pull --rebase,或开启「允许强制覆盖」后点 强制覆盖并重试
提示「强制推送被拒绝:预期值与远端不一致」 读取远端之后远端又变了(stale info) 再点一次「强制覆盖并重试」即可(会重新读取远端 sha)
提示「远端校验不一致」 分支保护改写了提交 / remote.origin.pushurl 指向别的仓库 / 远端被同时推送 打开仓库确认实际内容;这类情况插件按失败处理,不会假装成功
提示「疑似机密文件将被提交」 目录里有 .env、私钥等 确认要发布就忽略;不想发就加进 .gitignore 再重试
推送提示 could not read Username 没令牌且系统凭据不可用 保存令牌,或关闭 DSH_GITHUB_PUBLISH_NO_CREDENTIAL_HELPER 限制
拒绝发布「位于更大的仓库内」 插件目录在某个仓库的子目录里 勾选「只发布这个子目录」,或把插件移出该仓库
提交身份报错 本机没配 git user.name/user.email 用 GitHub 令牌时插件会自动兜底;否则 git config --global user.name/user.email
发布成功但远端没有文件 目录被 .gitignore 全部忽略 看任务日志里的「已暂存 N 处改动」与 .gitignore

已知边界

  • 一个插件一个仓库:<owner>/<repo> 由模板决定,不做 monorepo 子目录推送(需要的场景可以用「扫描根目录 + 手动目录」分别发布)。
  • 不发 Release 资产:只创建标签与 Release 记录,不上传构建产物。
  • 不建组织:组织必须已存在且令牌有建仓权限。
  • 不做 submodule / subtree / LFS 特殊处理:按普通 Git 仓库推送;.gitattributes 里的 LFS 规则由本机 git-lfs 处理。
  • 只服务本机回环:接口有意不接受非回环请求,即使 DSH 的 web server 绑定了 0.0.0.0。
  • 不做自动重试与后台队列:一次点击一个任务(同一目录同时只允许一个任务,路径按 realpath 归一化比较,重复点击返回 409),失败后由人决定是否重试(可强制覆盖或取消)。
  • 接口只防浏览器不防本机进程:回环 + 自定义头挡得住跨站请求,但本机任何进程都能调用这套 API(它本来就是本机工具);因此参数校验与 -- 隔离是硬要求,而不是可选项。
  • --force-with-lease 只保护「读到的那一刻」:读取远端 sha 与推送之间仍有极小窗口,中间人推送会被拒绝而不是被覆盖(这是设计取向)。
—/ 5

No ratings yet

Verified DSH bundle

Commit 8281be51ac0e

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout