Jira-Scripte/Console - Maintenance/Unused Screen Schemes cleaner.groovy
2025-12-14 19:17:45 +01:00

136 lines
4.9 KiB
Groovy
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import groovy.json.JsonOutput
// -------------------------- Konfiguration --------------------------
boolean DRY_RUN = true // erst prüfen; auf false stellen, wenn die Kandidatenliste stimmt
int PAGE_SIZE = 50
Set<String> EXCLUDE_BY_NAME = [
'Default Screen Scheme'
] as Set
// -------------------------- Logging -------------------------------
void logInfo(String msg){ try { logger.info(msg) } catch(e){ println msg } }
void logWarn(String msg){ try { logger.warn(msg) } catch(e){ println "WARN: " + msg } }
void logErr (String msg){ try { logger.error(msg)} catch(e){ println "ERR: " + msg } }
// -------------------------- HTTP Helpers --------------------------
Map getAsMap(String path, Map<String,Object> q=[:]) {
def req = get(path)
q.each { k,v -> req = req.queryString(k, v) }
def resp = req.asObject(Map)
if (resp.status != 200) {
throw new RuntimeException("GET " + path + " failed: HTTP " + resp.status + " :: " + resp.body)
}
return (resp.body ?: [:]) as Map
}
List<Map> pagedGetValues(String path, int pageSize) {
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
}
return all
}
// -------------------------- Fetchers ------------------------------
List<Map> fetchScreenSchemes(int pageSize) {
logInfo("Lade Screen Schemes…")
List<Map> list = pagedGetValues("/rest/api/3/screenscheme", pageSize)
logInfo("Screen Schemes gefunden: " + list.size())
return list
}
List<Map> fetchIssueTypeScreenSchemes(int pageSize) {
logInfo("Lade Issue Type Screen Schemes…")
List<Map> list = pagedGetValues("/rest/api/3/issuetypescreenscheme", pageSize)
logInfo("Issue Type Screen Schemes gefunden: " + list.size())
return list
}
/**
* Holt alle Referenzen auf Screen Schemes:
* - defaultScreenSchemeId je ITSS
* - globale ITSS→IssueType→ScreenScheme Mappings aus /issuetypescreenscheme/mapping
*/
Set<Long> fetchReferencedScreenSchemeIds(int pageSize, List<Map> itssList) {
Set<Long> refs = new LinkedHashSet<>()
// 1) Default-Zuordnungen je ITSS
for (Map itss : itssList) {
def defId = itss.get("defaultScreenSchemeId")
if (defId != null) refs.add(Long.valueOf(defId.toString()))
}
// 2) Globale Mappings
logInfo("Lade globale IssueType→ScreenScheme Mappings…")
List<Map> globalMaps = pagedGetValues("/rest/api/3/issuetypescreenscheme/mapping", pageSize)
for (Map m : globalMaps) {
def ssId = m.get("screenSchemeId")
if (ssId != null) refs.add(Long.valueOf(ssId.toString()))
}
logInfo("Referenzierte Screen Schemes gesamt: " + refs.size())
return refs
}
// -------------------------- Delete -------------------------------
boolean deleteScreenScheme(long id, String name) {
def resp = delete("/rest/api/3/screenscheme/" + 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)
return false
}
// -------------------------- Main -------------------------------
void runCleanup(boolean dryRun, int pageSize, Set<String> excludeByName) {
List<Map> screenSchemes = fetchScreenSchemes(pageSize)
if (screenSchemes.isEmpty()) { logInfo("Keine Screen Schemes vorhanden nichts zu tun."); return }
List<Map> itssList = fetchIssueTypeScreenSchemes(pageSize)
Set<Long> referenced = fetchReferencedScreenSchemeIds(pageSize, itssList)
List<Map> candidates = []
for (Map s : screenSchemes) {
long id = Long.valueOf(s.get("id").toString())
String name = (s.get("name") ?: "") as String
if (!referenced.contains(id) && !excludeByName.contains(name)) {
candidates.add([id: id, name: name, description: (s.get("description") ?: "")])
}
}
candidates.sort { a, b -> a.name <=> b.name }
if (candidates.isEmpty()) { logInfo("Keine ungenutzten Screen Schemes gefunden. ✅"); return }
logWarn("Ungenutzte Kandidaten (" + candidates.size() + "):")
for (Map c : candidates) logWarn(" - [" + c.id + "] " + c.name)
if (dryRun) {
logInfo("Dry-Run aktiv → nichts gelöscht.")
logInfo("JSON Dump:\n" + JsonOutput.prettyPrint(JsonOutput.toJson(candidates)))
return
}
int deleted = 0, skipped = 0
for (Map c : candidates) {
try {
if (deleteScreenScheme((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 ----
runCleanup(DRY_RUN, PAGE_SIZE, EXCLUDE_BY_NAME)