SonarQube删除项目全链路:权限、Web API与批量清理
1. 删除之前先弄明白一个 Sonar 项目到底由哪些东西构成后台和群里被问得最多的一类问题就是“SonarQube 里怎么把一个项目删掉”。听起来是个按钮级别的小事但我见过的实际情况是十个动手的人里至少有六七个会卡住要么菜单找不到要么点了没反应要么删掉之后第二天扫描一跑又原封不动冒出来。这些坑基本都源于同一个原因——把 SonarQube 里的“项目”当成了一个文件夹以为右键删除就完事其实它是projectKey作为主键串起来的一整组互相引用的数据。这篇就把 SonarQube下称 Sonar删除项目的完整链路拆开讲一是弄清删除动作的真实语义二是把权限和版本差异这两道门槛说明白三是给出图形界面、批量清理、Web API 三条可复现的路径四是把删除之后的连锁反应和常见故障整理成速查表。不管你是刚接手 Sonar 服务的新人还是要在几十上百个历史项目里做一次清理的老手这里面的细节应该都能直接用上。1.1 项目在 Sonar 里究竟是哪些数据的集合先建立一个准确的认知模型。你在 Sonar 界面上看到的那个项目条目背后对应的是一组表项目主体记录保存 key、名称、可见性、组织归属、分析快照与分支记录、指标度量数据、问题清单issue、质量配置与质量门的绑定关系、权限组与授权关系、项目级 Webhook、项目级令牌、用户订阅的通知设置以及后台任务队列里跟它相关的计算引擎Compute Engine简称 CE任务。除此之外还有一份同步到搜索引擎里的索引数据用于支撑看板检索和问题筛选。这里有一个非常容易被误解的点值得单独强调Sonar 从来不托管你的源码。它只保存分析结果也就是规则命中情况、覆盖率数字、重复率统计这类派生数据源码本身始终在你的代码仓库里。所以“删除 Sonar 项目”这个动作从头到尾都不会碰到你的 Git 仓库、不会删掉你的代码分支、不会影响你的构建产物。我见过有同事因为担心误删代码而不敢操作其实完全没必要——真正需要担心的是历史质量数据的不可恢复性而不是代码资产。反过来说正因为 Sonar 只存派生数据删除操作也就意味着这一段质量历史彻底断了。项目重新创建之后看板上那条趋势线会从零开始之前的覆盖率变化、技术债曲线、问题收敛过程全部清零并且没有办法通过重新扫描找回。这一点在清理生产环境项目时必须有清醒预期。1.2 Disable、Delete、Purge、停扫描四件事别混为一谈实操中出现的很多“删不掉”本质是把几个不同语义的动作混在了一起。先把它们分清楚动作真实含义数据是否保留典型使用场景停止扫描从 CI 流水线里去掉分析步骤或加跳过开关全部保留只是不再更新项目还在但暂时不做质量卡点删除项目从 Sonar 中移除项目及其全部关联数据基本不可恢复项目下线、仓库废弃、key 规划错误清理历史按保留策略清掉过期快照项目还在项目保留历史缩短数据库膨胀想减小存储占用取消授权收回项目权限但项目仍在全部保留交接、权限收敛、安全审计“停止扫描”这一项经常被忽略。如果你的诉求只是“这个项目不要再出现在看板上、不要再触发质量门拦截”最轻的做法是在流水线里给分析步骤加一个显式跳过条件比如在 Maven 或 Gradle 参数里传-Dsonar.skiptrue或者在 CI 的任务配置里把分析步骤设为条件执行。这种做法零风险、可回滚而且不需要动 Sonar 服务端。只有当项目确定彻底废弃、或者 key 命名本身规划错了必须重来时才轮到真正的删除。至于“清理历史”它对应的是 Sonar 内置的保留策略机制Housekeeping可以在全局配置里设定快照保留天数、按时间窗口保留等规则让后台定期收缩数据量。这跟删除项目完全是两回事但效果上都能缓解存储压力所以经常被拿来当替代方案。1.3 为什么不建议绕过服务直接动数据库总有人图省事想直接连上 Sonar 的数据库DELETE FROM projects WHERE kee xxx一把梭。我的态度很明确不要这么干。原因有三层。第一层是级联关系不可控。项目相关的数据分散在十几张表里外键约束、级联删除规则在不同大版本之间会调整手工删主表极可能留下一堆孤儿记录这些孤儿记录会在后续的报表统计、项目计数、权限校验里制造莫名其妙的偏差而且很难定位。第二层是搜索引擎索引不同步。Sonar 的检索能力依赖一份独立的索引数据数据库删了但索引没重建界面上依然能搜到那个项目点进去一片空白或者直接 500。要恢复只能触发整体重新索引而这个动作在大规模实例上耗时相当可观。第三层是缓存与权限。服务端有内存缓存删库之后不重启缓存里的项目信息还会存活一段时间表现出来的现象就是“删了但还在”。所以正确做法永远是走官方提供的界面入口或 Web API让服务自己完成级联清理和索引更新。数据库层面的操作最多只用于事后核查有没有残留绝不能作为删除手段。2. 权限与版本决定你能不能点下那个按钮的两道门槛把数据模型搞清楚之后下一个拦路虎就是权限和版本。我统计过自己处理过的求助超过一半的“找不到删除入口”根因不是操作错误而是账号权限不够或者版本路径不同。这两件事必须先确认否则后面所有步骤都是空谈。2.1 权限链路拆解你到底需要哪一级授权Sonar 的权限模型是分层的大致可以理解为系统级管理员权限管全局配置和所有项目项目级管理员权限只管被授权的那个项目普通用户只能看和改自己有权访问的内容。删除项目属于破坏性操作通常要求你具备系统级管理权限或者至少对目标项目持有管理权限。不同版本对这个判断的严格程度有细微差别稳妥做法是直接用系统管理员账号操作不要在这上面省事。实际操作中还有一个非常隐蔽的坑用令牌调接口时的权限与网页登录的权限是两套。你在浏览器里能看到的删除按钮不代表你手上的那个分析令牌也有删除权限。分析令牌默认只用于提交扫描结果不具备管理能力拿它去调删除接口返回的会是权限不足。这一点我在自动化脚本里踩过一次排查了小半天才发现是令牌类型选错了。另一个常见问题是外部身份认证对接。如果 Sonar 接入了统一登录账号的权限往往来自用户组映射改动权限要回到身份源那边调整而不是在 Sonar 里点几下就行。遇到“明明在组里但按钮还是灰的”先确认组映射是否生效、是否需要重新登录刷新会话。2.2 版本差异导致入口路径完全不同Sonar 的版本迭代对界面结构调整相当频繁删除项目的入口在各版本里位置不一样。下面这张对照表是我按实际接触过的版本整理的入口名称可能有细微出入但大方向是准的版本区间典型入口路径批量删除支持6.x 及更早系统管理 → 项目 → 管理支持勾选多个后批量删7.x系统管理 → 项目 → 管理支持勾选批量删8.x系统管理 → 项目单项目右侧操作菜单里有删除项单项目为主批量靠接口9.x系统管理 → 项目 → 管理选中后点删除支持10.x 及更新系统管理 → 项目 → 管理也可进入项目自身的管理页找到删除入口支持如果你的实例经过多次升级界面元素可能是混合的别死记路径先记住一个判断方法只要能进到“系统管理 → 项目”这一级看到项目列表删除入口就一定在这个列表的某个操作菜单里。实在找不到就用下面的接口自查返回结果里能看到项目 key再回头在界面上按 key 搜索。curl -s -u $SONAR_TOKEN: \ http://your-sonar-host:9000/api/projects/search?ps100 \ | head -c 2000云端版本的路径与自建版本不同一般在组织下的项目列表里选中项目进入其管理页后才有删除选项而且权限由组织角色控制这一点在跨环境操作时容易搞混。2.3 动手前的核对清单删除是不可逆操作我习惯在动手前跑一遍固定清单这个习惯帮我避免过至少两次误删。确认项目 key不要按项目显示名判断显示名可以重名key 才是唯一标识。先把 key 复制出来核对一遍。确认最近分析时间如果这个项目上周还有扫描提交说明还有人在用先联系确认再删。确认是否被质量门当作卡点有些流水线会把特定项目作为准入依据删掉会导致构建失败。确认是否有项目级 Webhook 对接了外部系统删除后外部系统会收不到通知可能引发数据断流。有条件的话先做一次数据库备份或整实例快照这是唯一的后悔药。先在测试实例上完整走一遍流程确认版本路径和权限都没问题。3. 实操三条路径把一个 Sonar 项目删干净准备工作做完进入正题。我把删除方式分成三种图形界面单删、界面批量清理、接口脚本化。前两种适合偶尔操作第三种适合站点维护和定期清理。三条路径的底层调用是同一套服务端逻辑效果一致选哪个取决于你要删几个。3.1 图形界面删单个项目这是最直观的路径适合一次删一两个的情况。用具备管理权限的账号登录进入系统管理找到项目列表页用项目 key 搜索定位到目标项目打开它的操作菜单选择删除。界面通常会弹一个二次确认要求你手动输入项目 key 或者点确认按钮这是防误触设计别嫌麻烦。删除请求提交后服务端不是瞬间完成的。它会先把项目标记为删除状态然后把关联数据的清理任务丢进后台队列。这时候你刷新列表可能还看不到变化属于正常现象。判断是否真的完成有两个观察点一是项目列表里搜不到了二是后台任务页面里相关的清理任务已经跑完且状态为成功。这里有个实操细节值得说如果目标项目存在正在进行中的分析任务删除会被拒绝或者排队等待。遇到提示“有任务正在执行”正确做法是等它跑完或者在后台任务页里把这个项目的待处理任务先取消掉再重新发起删除。硬等也是一种办法但高峰期可能等很久。3.2 界面批量清理历史项目站点运行两三年之后废弃项目会积累成一堆一个一个删能删到怀疑人生。这时候用批量入口效率高得多。在项目列表页一般支持按名称、key、最近分析时间、可见性等条件筛选筛出目标集合后勾选一次性提交删除。批量操作有两个必须注意的地方。第一是筛选条件一定要复核尤其是按“最近分析时间早于某天”这种模糊条件筛选时很容易把还在用但很久没扫的项目一起圈进去。我的做法是先筛出来把 key 列表导出到文本里人工过一遍确认无误再提交。第二是批量提交的数量建议分批一次几十个比一次几百个稳服务端压力小失败了也容易定位是哪一批出的问题。如果需要更精细的筛选界面能力可能不够用比如按项目前缀前缀匹配、按组织归属过滤。这时候直接上接口会更省事可以先用查询接口把目标 key 拉成列表人工核对后再喂给批量删除接口。3.3 用 Web API 把删除动作脚本化日常维护里我用得最多的还是接口因为可复现、可审计、可以塞进定期清理任务。核心接口是项目删除接口参数就是项目 key。# 删除单个项目 curl -s -u $SONAR_TOKEN: -X POST \ http://your-sonar-host:9000/api/projects/delete \ -d projectmy-service-api批量删除接口在较新版本里可用接受逗号分隔的 key 列表或者配合筛选参数使用# 按 key 列表批量删除 curl -s -u $SONAR_TOKEN: -X POST \ http://your-sonar-host:9000/api/projects/bulk_delete \ -d projectssvc-legacy-a,svc-legacy-b,svc-legacy-c # 按条件筛选后批量删除示例分析时间早于指定日期且从未被扫描过的项目 curl -s -u $SONAR_TOKEN: -X POST \ http://your-sonar-host:9000/api/projects/bulk_delete \ -d analyzedBefore2021-01-01 \ -d onProvisionedOnlytrue写脚本时有几个工程化要点都是实际踩出来的。一是结果校验接口返回成功不代表任务跑完要再查一次项目列表确认 key 已经消失或者去后台任务接口看清理任务状态。二是失败重试要谨慎网络抖动导致超时的时候请求可能已经提交成功了无脑重试可能报“项目不存在”这不代表操作失败要先查状态再决定。三是令牌权限前面提过这里再强调一次用于删除的令牌必须是管理类令牌分析令牌不行。下面这个是我常用的清理脚本骨架逻辑是先查、再核、后删、最后验#!/usr/bin/env bash set -euo pipefail HOSThttp://your-sonar-host:9000 TOKEN${SONAR_TOKEN:?需要先导出管理令牌} PREFIXlegacy- # 1. 拉取候选项目 curl -s -u $TOKEN: $HOST/api/projects/search?ps500 \ | python3 -c import sys, json data json.load(sys.stdin) for p in data.get(components, []): if p[key].startswith($PREFIX): print(p[key]) candidates.txt # 2. 人工核对 candidates.txt 后再继续 # 3. 逐个删除并记录结果 while read -r key; do [ -z $key ] continue code$(curl -s -o /dev/null -w %{http_code} -u $TOKEN: \ -X POST $HOST/api/projects/delete -d project$key) echo $key,$code delete-result.csv done candidates.txt # 4. 复查 curl -s -u $TOKEN: $HOST/api/projects/search?ps500 \ | grep -c \key\:\$PREFIX || echo 候选已清空把候选生成和实际删除分成两步、中间留一个人工核对环节这一步看起来笨但它是防止批量误删的唯一有效手段。我见过不止一次脚本写得太顺手条件一改直接把整个业务线的项目全删了的案例。4. 删除之后的连锁反应哪些会跟着消失哪些不会删完不是终点。真正让新手困惑的往往是删除之后的各种“异常”外部系统告警了、链接打不开了、看板上某个数字对不上了。提前知道哪些会受牵连可以把这些困惑消灭在事前。4.1 会随项目一起失效的东西项目被删除后下面这些会直接失效需要同步通知相关方项目级 Webhook外部系统再也收不到这个项目的分析结果推送如果对方依赖这个数据做报表或卡点会出现数据断流。项目级令牌如果之前给某个项目单独签发过分析令牌删除后该令牌失效对应的流水线会在下一次扫描时报认证失败。徽章链接README 里嵌的质量徽章会变成无效图片虽然不影响构建但观感很差建议同步清理。通知订阅用户对该项目的邮件或站内通知订阅一并消失不会残留。质量门绑定项目与质量门的关联关系消失但质量门本身是全局配置不受影响。权限授权项目级的用户和组授权关系清空重新创建同名项目需要重新授权。4.2 不会跟着消失、但容易让人误判的东西有几样东西看着像是应该被删掉实际上不会被误解的频率很高第一是扫描器配置。项目在 CI 里的sonar.projectKey、sonar.projectName、覆盖率报告路径这些配置属于代码仓库Sonar 服务端完全不知道。删完项目后流水线里的分析步骤还在下一次构建跑起来Sonar 检测到这个 key 不存在且提交者有创建权限就会自动把项目建回来。这就是“删了又复活”的头号原因第四章会专门讲怎么处理。第二是全局质量配置。语言级别的质量配置、规则集、质量门都是全局资源删除项目不会动它们。如果这个项目当时是某个质量配置的唯一样本删掉之后配置还在只是没有项目在用。第三是历史构建产物里的报告。流水线里生成的分析报告文件、覆盖率 XML 之类属于 CI 侧的产物Sonar 不管。要清理得去流水线产物那边处理。第四是外部系统里已经落库的数据。如果之前通过 Webhook 把数据同步到了某个看板或数据仓库Sonar 侧删除不会回溯清理对方的数据。这类数据要不要清、怎么清得跟对方系统的人一起定。5. 常见问题与排查实录这一节是我把这些年遇到的真实故障按现象归了类做成可以直接对照的速查表后面再展开几个高频问题的具体排查思路。现象最可能的原因处理方向界面里找不到删除入口权限不足或版本路径不同换管理员账号按版本查入口用接口兜底删除按钮灰色不可点项目有进行中的分析任务等待任务完成或先取消任务接口返回权限错误用了分析令牌而非管理令牌重新签发管理类令牌删除后项目又出现流水线仍在跑扫描自动创建同步修改或停用 CI 中的分析步骤删除成功但列表还能搜到索引尚未刷新等待后台任务完成必要时触发索引重建删除后报表数字对不上外部系统数据未同步清理与下游系统协同清理批量删除了不该删的项目筛选条件过宽立刻从备份恢复这是唯一有效手段5.1 “删完第二天又回来了”——自动创建的机制与对策这是被问得最多的一个。现象是昨天明明删干净了今天早上看板上又出现了数据从零开始。原因不复杂流水线里的分析步骤照常在跑Sonar 在接收分析结果时如果发现提交的 key 不存在且提交账号具备创建项目的权限就会按默认配置自动创建项目。这是设计上的便利特性但对清理场景来说就是噩梦。处理思路有两条选一条执行。第一条是从源头掐断在 CI 配置里把 Sonar 分析步骤移除或者加一个显式的跳过条件比如在 Maven 项目里传-Dsonar.skiptrue在 Gradle 里加-x sonar之类的跳过参数在流水线配置里直接把这一步注释掉。第二条是收紧自动创建权限把普通分析账号的“创建项目”权限收回只有管理员能新建。后者更适合大团队因为它同时能防止 key 拼错导致的重复项目问题副作用是新人接入时需要管理员协助创建。我一般建议两条一起上先收权限止血再改流水线彻底断根。只改流水线不收权限的话别的仓库哪天抄了旧模板同样的坑还会再踩一次。顺带说一个相关的坑改项目 key 等于新建项目。有人想“把 key 从old-name改成new-name”直接在 CI 里改了参数重新扫描结果 Sonar 里多了一个新项目旧项目还挂在那儿。正确的做法是先确认旧项目的数据是否要保留如果要保留就走改名相关的能力部分版本支持需要管理员操作如果不需要保留就直接删旧建新别用改参数的方式蒙混。5.2 删除卡住不动、后台任务异常另一种高频故障是删除请求提交了但没反应。排查顺序我固定按这个走第一步看后台任务页面找这个项目的清理任务看状态是排队中、执行中还是失败。排队中说明前面有任务在堵等执行中说明在处理也等失败的话看错误信息。第二步看是不是有并发的分析任务。如果同一个项目正好在跑扫描删除任务可能会等待或冲突。处理方式是先取消待处理的分析任务再重新发起删除。第三步看服务端资源。清理任务涉及大量数据删除和索引更新如果服务端内存或磁盘紧张任务会卡住或者反复重试。这时候去查看服务日志里有没有超时、连接池耗尽之类的报错必要时重启服务让队列重新调度。第四步才是怀疑数据一致性。如果日志里出现索引相关的异常说明索引和数据库可能不同步了需要触发索引重建。这个动作在大实例上耗时较长建议放到低峰期做并且提前通知使用方。5.3 删除操作的可审计性怎么保证删除是不可逆的所以留痕很重要。我的做法是在脚本里强制记录三样东西执行时间、执行人、被删项目的 key 列表和接口返回码统一落到一个 CSV 或者日志文件里按月份归档。这样做有两个好处一是事后追溯“某个项目是谁什么时候删的”能立刻查出来二是批量操作出问题时能精确定位影响范围。如果团队规模较大还可以把删除动作做成审批流业务方提申请管理员在低峰期集中执行执行完回执。这套流程听起来重但比出事后恢复数据要轻得多。毕竟前面说过Sonar 侧的数据删了基本没有恢复手段唯一的保底方案是数据库备份或者实例快照而备份的恢复粒度是整实例为了一个项目回滚整个实例的代价非常大。6. 几条从踩坑里换来的建议先说一个我早期犯过的错可能很多人都正在犯把项目 key 当成可以随便改的东西。当时为了统一命名规范我把一批项目的 key 在 CI 里批量改了前缀重新扫结果 Sonar 上多出一倍的项目旧的删、新的建历史趋势图全部断档还被业务方问了好几次为什么覆盖率突然归零。后来我把规则定死项目 key 一旦创建就不再变更命名规范在创建前就确定好宁可创建时多花五分钟评审也不要上线后改。再一条是关于“清理”这件事本身的节奏。我的建议是不要攒着攒到几百个项目一起清的时候风险和成本都会成倍上升。比较舒服的做法是每个季度花半小时过一遍项目列表按最近分析时间排序把超过半年没动的项目列出来跟业务方确认一遍确认废弃的当月就删掉。这样每次的处理量都很小每一笔都能说清楚为什么删也不会出现“不知道这个项目是谁的所以不敢删”的僵局。还有个小技巧分享一下。如果你不确定某个项目到底还有没有人用别急着删先做一步降级观察把项目的可见性收窄或者在 README 里把徽章撤掉观察一两个月有没有人来问。没人问说明确实废弃了再删也不迟。这比直接删掉之后被人追着要数据要友好得多也给自己留了缓冲。最后提一句存储。项目删了之后数据库文件的物理占用不会立刻下降因为数据是标记删除加后台回收的而且索引文件也需要时间整理。如果你是为了腾空间才做清理别指望删完立刻看到磁盘曲线掉下来通常要等后台任务跑完、索引刷新之后才见效大的实例可能要等上几个小时到一天。真要压缩空间还得配合数据库层面的常规维护动作这个属于另一摊事就不在这里展开了。