116 lines
4.2 KiB
Groovy
116 lines
4.2 KiB
Groovy
import groovy.json.JsonOutput
|
||
|
||
// ------------------ Konfig ------------------
|
||
boolean DRY_RUN = true
|
||
int PAGE_SIZE = 50
|
||
Set<String> EXCLUDE_BY_NAME = [] as Set // optional Namen schützen
|
||
|
||
// ------------------ Logging -----------------
|
||
void logInfo(String m){ try{ logger.info(m) }catch(e){ println m } }
|
||
void logWarn(String m){ try{ logger.warn(m) }catch(e){ println "WARN: " + m } }
|
||
void logErr (String m){ try{ logger.error(m)}catch(e){ println "ERR: " + m } }
|
||
|
||
// ------------------ HTTP Helpers -----------
|
||
Map getAsMap(String path, Map q=[:]) {
|
||
def req = get(path)
|
||
q.each{ k,v -> req = req.queryString(k, v) }
|
||
def r = req.asObject(Map)
|
||
if (r.status != 200) throw new RuntimeException("GET " + path + " failed: HTTP " + r.status + " :: " + r.body)
|
||
(r.body ?: [:]) as Map
|
||
}
|
||
List<Map> pagedGetValues(String path, int pageSize=50) {
|
||
int startAt = 0; List<Map> all = []
|
||
while (true) {
|
||
Map body = getAsMap(path, [startAt:startAt, maxResults:pageSize])
|
||
List vals = (body.values ?: []) as List
|
||
all.addAll(vals as List<Map>)
|
||
int total = (body.total ?: (startAt + vals.size())) as int
|
||
int nextStart = startAt + ((body.maxResults ?: vals.size()) as int)
|
||
if (vals.isEmpty() || nextStart >= total) break
|
||
startAt = nextStart
|
||
}
|
||
all
|
||
}
|
||
|
||
// ------------------ Fetchers ----------------
|
||
List<Map> fetchITSS(int pageSize){
|
||
logInfo("Lade Issue Type Screen Schemes…")
|
||
def list = pagedGetValues("/rest/api/3/issuetypescreenscheme", pageSize)
|
||
logInfo("ITSS gefunden: " + list.size())
|
||
list
|
||
}
|
||
List<Map> fetchProjects(int pageSize){
|
||
logInfo("Lade Projekte…")
|
||
def list = pagedGetValues("/rest/api/3/project/search", pageSize)
|
||
logInfo("Projekte gefunden: " + list.size())
|
||
list
|
||
}
|
||
Long fetchITSSForProject(Long projectId){
|
||
// Liefert die ITSS-ID, die einem Projekt zugewiesen ist
|
||
def m = getAsMap("/rest/api/3/project/${projectId}/issuetypescreenscheme")
|
||
def id = m.get("issueTypeScreenSchemeId")
|
||
return (id == null ? null : Long.valueOf(id.toString()))
|
||
}
|
||
|
||
// ------------------ Delete ------------------
|
||
boolean deleteITSS(long id, String name){
|
||
def resp = delete("/rest/api/3/issuetypescreenscheme/${id}").asString()
|
||
if (resp.status in [200,204]) { logInfo("Gelöscht: [${id}] ${name}"); return true }
|
||
logWarn("Nicht gelöscht [${id}] ${name} :: HTTP ${resp.status} :: ${resp.body}")
|
||
false
|
||
}
|
||
|
||
// ------------------ Main --------------------
|
||
void runITSSCleanup(boolean dryRun, int pageSize, Set<String> excludeByName){
|
||
def itss = fetchITSS(pageSize)
|
||
if (itss.isEmpty()){ logInfo("Keine ITSS vorhanden – nichts zu tun."); return }
|
||
Map<Long,Map> itssById = [:]
|
||
itss.each{ Map x -> if (x.id!=null) itssById[Long.valueOf(x.id.toString())] = x }
|
||
|
||
def projects = fetchProjects(pageSize)
|
||
Set<Long> referenced = new LinkedHashSet<>()
|
||
projects.each{ Map p ->
|
||
def pid = p.get("id"); if (pid==null) return
|
||
try {
|
||
Long ref = fetchITSSForProject(Long.valueOf(pid.toString()))
|
||
if (ref!=null) referenced << ref
|
||
} catch (Exception ex) {
|
||
logWarn("ITSS-Mapping für Projekt ${p.key ?: pid} nicht lesbar: " + ex.message)
|
||
}
|
||
}
|
||
logInfo("Referenzierte ITSS gesamt: " + referenced.size())
|
||
|
||
List<Map> candidates = []
|
||
itssById.each{ Long id, Map row ->
|
||
String name = (row.name ?: "") as String
|
||
if (!referenced.contains(id) && !excludeByName.contains(name)){
|
||
candidates << [id:id, name:name, description:(row.description ?: "")]
|
||
}
|
||
}
|
||
candidates.sort{ a,b -> a.name <=> b.name }
|
||
|
||
if (candidates.isEmpty()){ logInfo("Keine ungenutzten ITSS gefunden. ✅"); return }
|
||
|
||
logWarn("Ungenutzte ITSS (${candidates.size()}):")
|
||
candidates.each{ c -> logWarn(" - [${c.id}] ${c.name}") }
|
||
|
||
if (dryRun){
|
||
logInfo("Dry-Run aktiv → nichts gelöscht.")
|
||
logInfo("JSON:\n" + JsonOutput.prettyPrint(JsonOutput.toJson(candidates)))
|
||
return
|
||
}
|
||
|
||
int deleted=0, skipped=0
|
||
candidates.each{ c ->
|
||
try {
|
||
if (deleteITSS((c.id as Long), c.name.toString())) deleted++ else skipped++
|
||
} catch (Exception ex){
|
||
skipped++; logErr("Fehler beim Löschen [${c.id}] ${c.name} :: " + ex.message)
|
||
}
|
||
}
|
||
logWarn("Fertig. Ergebnis: deleted=${deleted}, skipped=${skipped}")
|
||
}
|
||
|
||
// ---- Start ----
|
||
runITSSCleanup(DRY_RUN, PAGE_SIZE, EXCLUDE_BY_NAME)
|