↑ ↓ 移动 · Enter 打开 · Esc 关闭点按一个结果

架构
skill · build 类 · sonnet

sonarqube-setup

把项目接入 SonarQube 服务器:构建文件里的 scanner,以及一个 CI 步骤或一个本地容器。

skill · buildsonneteffort low3 允许的路径随生成的项目导出
§ skill 的定义

sonarqube-setup,逐节说明

skill 自己的 SKILL.md 的各个章节,节选。悬停或点击一个章节,查看它引用的项目规则、模板和 references/ 文件。章节也是标签:点击即可跳转。

nerviz/.claude/skills/sonarqube-setup/SKILL.md

## Why this is a skill (Form 1) and not Form 2

Form 1: project-bootstrap and arch-adopt chain it by name, and a skill with disable-model-invocation: true cannot be called through the Skill tool — the same reason git-publish and docker-architect stay model-invocable. Axis 2 of the interview — two callers, one of which runs inside a project where project-bootstrap does not exist — is what made it a skill of its own instead of a step inside the bootstrap. The closest rejected form was that step: arch-adopt would then have to write pom.xml and docker-compose.yml itself, widening its territory and giving the compose file a second owner. Record: @.claude/decisions/0091-sonarqube-setup-skill.md.

Runs on sonnet with effort: low: a fixed procedure behind one question, every version resolved by curl (@.claude/decisions/0080-procedural-skills-on-sonnet.md).

## Procedure

Project directory. When the caller's one-line context names project: <absolute path>, that directory is the project root, not the session's: read and write every path of this procedure under it, and run every shell command as (cd "<path>" && …). When this skill chains docker-architect, pass project: <path> on in its context.

### 1 · Detect the build tool and whether Sonar is already there

pom.xml at the root → maven. build.gradle at the root → gradle. Neither → stop: there is no project to configure. Both → stop and say so; this repo never generates both.

Grep the root build file for sonar-maven-plugin / org.sonarqube. Found → entry rule above: report and stop.

### 2 · Ask about the server — one `AskUserQuestion`

Two questions in the same call:

  1. Server — "Is there already a SonarQube server or SonarCloud organization for this
  2. Authentication — "How does the analysis authenticate?"

When a caller already passed an answer in its one-line context, don't ask it again.

### 3 · Write the build configuration

Resolve the scanner version on the repository itself — never from memory, same rule project-bootstrap/references/build-maven.md § 4.6 applies to every plugin:

# maven
curl -sS 'https://repo1.maven.org/maven2/org/sonarsource/scanner/maven/sonar-maven-plugin/maven-metadata.xml' | grep -o '<release>[^<]*</release>'
# gradle — the plugin lives on the Gradle Plugin Portal, not on Maven Central
curl -sS 'https://plugins.gradle.org/m2/org/sonarqube/org.sonarqube.gradle.plugin/maven-metadata.xml' | grep -o '<release>[^<]*</release>'

No network → don't write a version: say which one could not be resolved and let the user supply it.

Then merge — never replace what the root build file already has:

ToolExemplarWhere
maventemplates/pom-sonar.xml.exampleproperties into the root POM's <properties>, the plugin into <build><pluginManagement><plugins>
gradletemplates/build-gradle-sonar.examplethe plugin into the root plugins {}, the sonar {} block at the root level — not inside subprojects

sonar.projectKey is <groupId>:<artifactId> from the root build file. sonar.host.url is the URL from step 2, or http://localhost:9000 for the local container. sonar.organization only for SonarCloud. The coverage report path needs no property: the scanner's default report paths are exactly where the JaCoCo setup project-bootstrap writes puts them (target/site/jacoco/jacoco.xml, build/reports/jacoco/test/jacocoTestReport.xml).

… 此处截断,其余见文件

### 4 · Local server — only when step 2 answered *None*

Invoke docker-architect through the Skill tool with a one-line context: "service sonarqube, chained from sonarqube-setup — no engine question". It owns every service block in docker-compose.yml, and its template for this one is templates/sonarqube-service.yml.example there. Don't write the block here, not even when it looks like a copy: that is the ownership split, not a style choice.

No docker-compose.yml at the root → docker-architect stops on its own entry rule. Report that the build points at http://localhost:9000 and nothing runs there yet.

### 5 · CI — only when the server is external

A local container is unreachable from a CI runner, so this step runs only for an existing server or SonarCloud. Merge templates/ci-sonar-step.yml.example into .github/workflows/build.yml (the file project-bootstrap step 6.5 writes): job-level env, fetch-depth: 0 on the checkout, and the analysis step after verify. The step is skipped while the SONAR_TOKEN secret is absent — a fork's pull request has none, and a red build there would say nothing about the code.

No .github/workflows/build.yml → skip, and say it in the report.

## Report

✅ SonarQube configured — <existing server <url> | SonarCloud <org> | local container>

Build ....... <pom.xml | build.gradle> — scanner <version> (resolved from <repository>)
Project key . <groupId:artifactId>
Coverage .... sonar.coverage.exclusions = <patterns> (from the JaCoCo excludes in <file>:<line>) | not set — no JaCoCo excludes in <file>
Server ...... <url> · auth: <SONAR_TOKEN | anonymous>
Compose ..... <sonarqube added by docker-architect, tag <tag> | not touched — external server>
CI .......... <step added, runs when secret SONAR_TOKEN is set | skipped — local server | skipped — no workflow>

Access check (before any scan):
  curl -s <url>/api/authentication/validate      → {"valid":true} when anonymous is allowed
  (or SONAR_TOKEN is exported — the scanner reads it)

First run:  <./mvnw -B verify sonar:sonar | ./gradlew build sonar>
Rerun:      <./mvnw -B sonar:sonar | ./gradlew sonar>   — same build output, no tests again

The access check moves an authentication failure from the end of the scan to its start: validate answers in milliseconds, the scan fails only after verify has run every test. The rerun line is for a change on the server side — a setting, a permission, a quality profile — where the code and target/ (or build/) are unchanged.

… 此处截断,其余见文件

在 GitHub 上打开完整文件 →
§ 行为

它读什么、写什么、拒绝什么

skill 自己的契约,取自 SKILL.md,保留原文。

Class

build — the territory is skill_classes.build's override for this skill in @.claude/schemas/extensions.json: the root build file and .github/workflows/**, nothing else. ArchHook.java guard enforces it. docker-compose.yml is deliberately outside it.

Reads

the root pom.xml or build.gradle (coordinates, existing plugins, the JaCoCo excludes that sonar.coverage.exclusions is derived from), and .github/workflows/build.yml when it exists.

Writes

the scanner plugin and the sonar.* properties into the root build file — the only change to that file outside a feature round besides project-bootstrap's generation and the two installers' own setup — and the analysis step into the workflow when the server is external.

Never writes

a token, a password, or any credential value, in any file.

Delegates

the sonarqube compose service to docker-architect, the single owner of every service block.

Chained by

project-bootstrap (step 8.4, after Verify) and arch-adopt (step 7), and invocable by hand. Travels into the generated project (export.skills.include): arch-adopt runs inside the project, where project-bootstrap does not exist, and needs this skill there.

来源:SKILL.md § Contract(nerviz e4cae27)。

§ guard 与 hook

guard 如何处理它

build 是该 skill 的类,类中每个 skill 都有自己的领地。由谁调用决定适用哪个领地:在 project-initializer 之下用 agent 的;从主线程调用则根本不放行。右侧文字是 hook 自己的输出。

sonarqube-setup 类的 write_allow

  • ✓pom.xml
  • ✓build.gradle
  • ✓.github/workflows/**
  • 当 skill 自己打开阶段时,此列表之外的任何路径
  • /init-project 打开时来自主线程的调用

拒绝以退出码 2 结束,消息回到模型。

由 project-initializer 串联时,它的写入带着该 agent 的 agent_type,按 agent_classes.driver(**)判定。此列表仅在 skill 由自己的提示运行时适用。

PreToolUse  Skill(sonarqube-setup)
# agent_type: project-initializer (class driver)
  guard call                       exit 0
# a classed agent's call leaves the phase as it was
PreToolUse  Write(../bank-app/pom.xml)
  guard write                      exit 0
# judged by agent_classes.driver, write_allow: **
§ 编排

它在轨道上的位置

每张卡片是 /init-project 的一步。访谈在主会话中进行;着色的步骤在 project-initializer 内运行;git-publish 在 audit genesis 之后回到主会话。虚线步骤仅在适用时运行。

← 返回链路