@godv61/dsh-task-engine 0.30.2 → 0.30.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ import assert from 'node:assert/strict'
2
+ import { test } from 'node:test'
3
+ import { listSonarProjectRules, parseAnalyzedLanguages } from './lib/sonar-rule-catalog.js'
4
+
5
+ test('project rule browser selects analyzed languages and pages only the selected effective profile', async () => {
6
+ const requests = []
7
+ const fetcher = async (url, options) => {
8
+ requests.push({ url: new URL(url), authorization: options.headers.Authorization })
9
+ const path = new URL(url).pathname
10
+ const body = path.endsWith('/qualityprofiles/search') ? { profiles: [
11
+ { key: 'other-js', name: 'Sonar way', language: 'js', activeRuleCount: 382 },
12
+ { key: 'project-java', name: 'Routine Rule', language: 'java', activeRuleCount: 2 },
13
+ ] } : path.endsWith('/measures/component') ? { component: { measures: [
14
+ { metric: 'ncloc_language_distribution', value: 'java=100;xml=5' },
15
+ ] } } : Number(new URL(url).searchParams.get('p')) === 1
16
+ ? { total: 2, rules: [{ key: 'java:S100', name: 'One', severity: 'MAJOR' }] }
17
+ : { total: 2, rules: [{ key: 'mycompany-java:Custom', name: 'Two', severity: 'MINOR' }] }
18
+ return { ok: true, json: async () => body }
19
+ }
20
+ const view = await listSonarProjectRules('https://sonar.example', 'qms-key', 'secret', undefined, fetcher)
21
+ assert.equal(view.selected_language, 'JAVA')
22
+ assert.deepEqual(view.analyzed_languages, ['JAVA', 'XML'])
23
+ assert.equal(view.rules.length, 2)
24
+ assert.deepEqual(view.profiles.map(profile => [profile.language, profile.analyzed]), [['JAVA', true], ['JS', false]])
25
+ assert.ok(requests.every(request => request.authorization === 'Bearer secret'))
26
+ assert.equal(requests.find(request => request.url.pathname.endsWith('/qualityprofiles/search')).url.searchParams.get('project'), 'qms-key')
27
+ assert.equal(requests.find(request => request.url.pathname.endsWith('/measures/component')).url.searchParams.get('metricKeys'),
28
+ 'ncloc_language_distribution')
29
+ assert.ok(requests.filter(request => request.url.pathname.endsWith('/rules/search'))
30
+ .every(request => request.url.searchParams.get('qprofile') === 'project-java'
31
+ && request.url.searchParams.get('activation') === 'true'))
32
+ assert.equal(JSON.stringify(view).includes('secret'), false)
33
+ })
34
+
35
+ test('invalid project language cannot trigger a query for another profile', async () => {
36
+ const fetcher = async url => ({ ok: true, json: async () => new URL(url).pathname.endsWith('/qualityprofiles/search')
37
+ ? { profiles: [{ key: 'project-java', name: 'Routine Rule', language: 'java', activeRuleCount: 1 }] }
38
+ : { component: { measures: [] } } })
39
+ await assert.rejects(() => listSonarProjectRules('https://sonar.example', 'qms-key', 'secret', 'GO', fetcher),
40
+ /没有 GO 的 Quality Profile/)
41
+ assert.deepEqual(parseAnalyzedLanguages('java=120;xml=8;java=120'), ['JAVA', 'XML'])
42
+ })
@@ -4,10 +4,23 @@ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'
4
4
  import { tmpdir } from 'node:os'
5
5
  import { join } from 'node:path'
6
6
  import { test } from 'node:test'
7
- import { changedLines, missingLocalAuditScope, uncoveredLocalFiles } from './lib/sonarlint-local.js'
7
+ import { changedLines, currentBinding, missingLocalAuditScope, saveBinding, uncoveredLocalFiles } from './lib/sonarlint-local.js'
8
8
  import { isIncludedAuditPath, validAuditIncludePaths } from './lib/sonar.js'
9
9
  import { renderSonarReport, sonarReportPath } from './lib/sonar-report.js'
10
10
 
11
+ test('manual rule update persists the active project binding without leaving a staging file', () => {
12
+ const directory = mkdtempSync(join(tmpdir(), 'dsh-sonar-binding-'))
13
+ try {
14
+ assert.equal(currentBinding(directory), undefined)
15
+ const first = { connection_id: 'project-11111111-1111-4111-8111-111111111111', updated_at: '2026-10-08T01:00:00.000Z' }
16
+ const next = { connection_id: 'project-22222222-2222-4222-8222-222222222222', updated_at: '2026-10-08T02:00:00.000Z' }
17
+ saveBinding(directory, first)
18
+ assert.deepEqual(currentBinding(directory), first)
19
+ saveBinding(directory, next)
20
+ assert.deepEqual(currentBinding(directory), next)
21
+ } finally { rmSync(directory, { recursive: true, force: true }) }
22
+ })
23
+
11
24
  test('project scan roots mirror Maven backend scope without including the Vue app', () => {
12
25
  const roots = ['fp-bussiness', 'fp-ruoyi', 'fp-ruoyi-spring-boot', 'pom.xml']
13
26
  assert.equal(validAuditIncludePaths(roots), true)
package/README.md CHANGED
@@ -139,7 +139,11 @@ pnpm dsh web --no-open
139
139
 
140
140
  Sonar 的非秘密配置保存在项目 `.dsh/meta.json`;Token **不会写入该文件、任务或报告**。换项目时分别配置。项目的 Quality Profile 和规则本身仍由 SonarQube 服务器管理。
141
141
 
142
- 保存配置和 Token 后,管理员可点击**安装或检查本地分析器**。页面会显示当前项目各语言的生效规则数与分析器同步状态;这一步不审核或上传项目代码。首次下载完成后,后续任务的代码审核阶段会自动复用本机组件,即使管理员没有预先点击,审核时也会尝试自动准备。
142
+ 保存配置和 Token 后,管理员可点击**安装或检查本地分析器**。此操作按项目 Key 连接 SonarQube,检查本地组件并读取项目生效的 Quality Profile;页面优先显示该项目最近一次服务端分析涉及的语言,例如 QMS 的 Java、XML。若项目还没有分析记录,则显示所有已配置语言。它不会审核或上传项目代码;后续任务在代码审核阶段也会自动准备组件。
143
+
144
+ 服务器修改了当前项目的 Quality Profile 或规则参数后,点击**更新当前项目规则**。插件为这个项目建立新的本地绑定,重新同步服务器配置;只有本地同步成功,后续审核才切换到新绑定。页面显示上次手动更新时间;更新失败时继续使用上一次成功的绑定。这个操作不扫描或上传代码,也不计算服务端 Quality Gate。每个项目分别更新。
145
+
146
+ 点击**查看当前项目规则**可按语言查看对应 Quality Profile 的名称、启用规则总数、规则编号与名称,搜索规则并跳转到 SonarQube 规则详情。默认只显示项目最近一次分析涉及的语言,需要时可展开其它语言配置。此列表来自服务器当前配置;数量核对只证明规则清单读取完整,不能证明每条规则都可在本地执行。**Quality Profile** 决定启用哪些规则;**Quality Gate** 是服务端分析结果的通过条件,提交前本地检查不计算完整 Quality Gate。
143
147
 
144
148
  ### 提交前审核如何运行
145
149
 
package/docs/manual.html CHANGED
@@ -125,7 +125,9 @@ pnpm dsh web --no-open</code></pre>
125
125
  <tr><td>审核路径</td><td>高级设置;留空审核本次任务全部代码。若只需后端审核,填写项目相对目录。</td></tr>
126
126
  <tr><td>Token</td><td>当前项目单独保存;保存后只显示是否已配置,不会回显原值。</td></tr>
127
127
  </tbody></table></div>
128
- <p>保存项目配置和 Token 后,管理员可点击<strong>安装或检查本地分析器</strong>。页面显示各语言生效规则数与分析器状态;检查不会审核或上传项目代码。未预先点击也可以继续开发,代码审核时会自动准备本机组件。</p>
128
+ <p>保存项目配置和 Token 后,管理员可点击<strong>安装或检查本地分析器</strong>。检查按当前项目 Key 读取生效的 Quality Profile,优先显示该项目最近一次服务端分析涉及的语言;未分析过的项目显示所有已配置语言。检查不会审核或上传代码。未预先点击也可以继续开发,代码审核时会自动准备本机组件。</p>
129
+ <p>SonarQube 服务器更改了此项目的规则或参数后,点击<strong>更新当前项目规则</strong>。插件重新建立此项目的本地绑定并同步规则;成功后显示手动更新时间,后续代码审核使用新绑定。更新失败则继续使用此前成功的配置。更新不会扫描或上传代码,也不计算服务端 Quality Gate;其它项目不受影响。</p>
130
+ <p>点击<strong>查看当前项目规则</strong>,可按语言查看 Quality Profile 名称和启用规则清单,按编号、名称或严重性搜索,并打开 SonarQube 规则详情。默认只显示最近一次分析涉及的语言,也可展开其它语言配置。规则清单与本地分析器状态是两种信息:<code>SYNCED</code> 表示该语言分析器可用,不能证明清单中的每条规则都能在本地执行。Quality Profile 决定启用规则;Quality Gate 根据服务端扫描结果判断是否通过,提交前本地检查不计算完整 Quality Gate。</p>
129
131
  <h3>提交前审核如何运行</h3>
130
132
  <p>测试通过并进入代码审核阶段后,代码审核元技能自动调用 <code>dev_task sonar_check</code>。默认以 <code>HEAD</code> 为基线,分析任务中尚未提交的 Git 变更行;无需提交、推送或在页面手动发起。首次审核会下载并校验官方 SonarLint 后台组件,保存在运行 DSH 的用户目录 <code>~/.dsh/sonarlint-runtime/</code>;组件再从 SonarQube 同步当前项目的语言分析器和 Quality Profile 规则。首次安装要求可访问 Maven Central 和 SonarQube。通常无需安装 IDEA、Maven、SonarScanner 或单独配置 JDK;JS/TS/Vue/CSS 分析仍可能需要 Node.js。</p>
131
133
  <p>首次下载约 93 MiB,慢速网络可能等待较久。若运行 DSH 的机器访问 Maven Central 需要代理,请在启动 DSH 前设置 <code>DSH_SONARLINT_PROXY=http://127.0.0.1:7897</code>(替换为实际地址),或使用 <code>HTTPS_PROXY</code>,然后重启 DSH。代理是安装组件的本机网络设置,不写入项目配置。已经手工安装后台组件时,可选用 <code>DSH_SONARLINT_JAVA</code> 和 <code>DSH_SONARLINT_LIB</code> 指向现有安装。</p>