Jira-Scripte/Console - Maintenance/06. Unused Screen Schemes cleaner.groovy

181 lines
5.6 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.

/**
* Screen Scheme Housekeeping (Cloud) über Issue Type Screen Scheme Mappings (zuverlässig)
* --------------------------------------------------------------------------------------
* Löschen nur, wenn ein Screen Scheme nirgendwo in IssueTypeScreenScheme-Mappings referenziert ist.
* Jira verhindert Delete sowieso, wenn es noch referenziert wird. :contentReference[oaicite:2]{index=2}
*/
def PROJECT_KEYS = ["NIN","NICS","NINPDS","NINPDSARC","CS","CRON"]
def PROTECTED_SCHEME_IDS = [
// "1" // Default Screen Scheme ggf. hart schützen
]
def PROTECTED_NAME_PATTERNS = [
"default",
"system",
"jira"
]
def DRY_RUN = true
def PROJECT_PAGE_SIZE = 50
def SCREEN_SCHEME_PAGE_SIZE = 100
def MAPPING_PAGE_SIZE = 100
logger.info("=== Screen Scheme Housekeeping (via IssueTypeScreenScheme mappings) ===")
logger.info("Projects: ${PROJECT_KEYS}")
logger.info("Protected scheme IDs: ${PROTECTED_SCHEME_IDS}")
logger.info("Protected name patterns: ${PROTECTED_NAME_PATTERNS}")
logger.info("DRY_RUN: ${DRY_RUN}")
def isNameProtected = { String name ->
def n = (name ?: "").toLowerCase()
PROTECTED_NAME_PATTERNS.any { p -> n.contains((p ?: "").toLowerCase()) }
}
def isIdProtected = { String id ->
PROTECTED_SCHEME_IDS.any { it?.toString() == id?.toString() }
}
/**
* 1) Project Key -> Project ID
*/
def projectIds = [:] // key -> id
PROJECT_KEYS.each { key ->
def resp = get("/rest/api/3/project/${key}").asObject(Map)
if (resp.status == 200) {
projectIds[key] = resp.body?.id?.toString()
logger.info("INFO|PROJECT|${key}|id=${projectIds[key]}")
} else {
logger.warn("WARN|PROJECT_LOOKUP_FAILED|${key}|status=${resp.status}")
}
}
def validProjectIds = projectIds.values().findAll { it != null }.unique()
/**
* 2) Aus Projekten die IssueTypeScreenSchemeIds einsammeln
* GET /rest/api/3/issuetypescreenscheme/project?projectId=...
*/
def issueTypeScreenSchemeIds = [] as Set
validProjectIds.each { pid ->
def resp = get("/rest/api/3/issuetypescreenscheme/project?projectId=${pid}")
.asObject(Map)
if (resp.status != 200) {
logger.warn("WARN|ITSCS_FOR_PROJECT_FAILED|projectId=${pid}|status=${resp.status}")
return
}
(resp.body?.values ?: []).each { row ->
def itscsId = row?.issueTypeScreenScheme?.id?.toString()
if (itscsId) issueTypeScreenSchemeIds << itscsId
}
}
logger.info("INFO|ISSUETYPE_SCREENSCHEME_IDS|count=${issueTypeScreenSchemeIds.size()}|ids=${issueTypeScreenSchemeIds}")
/**
* 3) Für jedes IssueTypeScreenSchemeId: Mappings holen und alle screenSchemeIds sammeln
* GET /rest/api/3/issuetypescreenscheme/mapping?issueTypeScreenSchemeId=... (paging)
*/
def referencedScreenSchemeIds = [] as Set
issueTypeScreenSchemeIds.each { itscsId ->
def startAt = 0
while (true) {
def resp = get("/rest/api/3/issuetypescreenscheme/mapping?issueTypeScreenSchemeId=${itscsId}&startAt=${startAt}&maxResults=${MAPPING_PAGE_SIZE}")
.asObject(Map)
if (resp.status != 200) {
logger.warn("WARN|ITSCS_MAPPING_FAILED|itscsId=${itscsId}|status=${resp.status}")
break
}
def values = resp.body?.values ?: []
values.each { m ->
def screenSchemeId = m?.screenSchemeId?.toString()
if (screenSchemeId) referencedScreenSchemeIds << screenSchemeId
}
def isLast = resp.body?.isLast
if (isLast == true || values.isEmpty()) break
startAt += MAPPING_PAGE_SIZE
}
}
logger.info("INFO|REFERENCED_SCREENSCHEME_IDS|count=${referencedScreenSchemeIds.size()}")
/**
* 4) Alle Screen Schemes holen, Kandidaten bestimmen
*/
def allScreenSchemes = []
def startAt = 0
while (true) {
def resp = get("/rest/api/3/screenscheme?startAt=${startAt}&maxResults=${SCREEN_SCHEME_PAGE_SIZE}")
.asObject(Map)
if (resp.status != 200) {
logger.error("ERROR|SCREENSCHEME_LIST_FAILED|status=${resp.status}|body=${resp.body}")
break
}
def values = resp.body?.values ?: []
allScreenSchemes.addAll(values)
def isLast = resp.body?.isLast
if (isLast == true || values.isEmpty()) break
startAt += SCREEN_SCHEME_PAGE_SIZE
}
logger.info("INFO|TOTAL_SCREEN_SCHEMES|${allScreenSchemes.size()}")
def keptReferenced = 0
def keptProtected = 0
def candidates = [] // [id,name,reason]
allScreenSchemes.each { ss ->
def id = ss?.id?.toString()
def name = ss?.name?.toString()
def nameProtected = isNameProtected(name)
def idProtected = isIdProtected(id)
if (nameProtected || idProtected) {
keptProtected++
def why = []
if (idProtected) why << "ID_PROTECTED"
if (nameProtected) why << "NAME_PROTECTED"
logger.info("KEEP|screenSchemeId=${id}|name=${name}|reason=${why}")
return
}
if (referencedScreenSchemeIds.contains(id)) {
keptReferenced++
logger.info("KEEP|screenSchemeId=${id}|name=${name}|reason=REFERENCED_BY_ISSUETYPE_SCREENSCHEME")
return
}
logger.info("DEL?|screenSchemeId=${id}|name=${name}|reason=NOT_REFERENCED_ANYWHERE")
candidates << [id, name, "NOT_REFERENCED_ANYWHERE"]
if (!DRY_RUN) {
def delResp = delete("/rest/api/3/screenscheme/${id}").asString()
if (delResp.status == 204) {
logger.info("DEL|OK|screenSchemeId=${id}|name=${name}")
} else {
logger.error("DEL|FAIL|screenSchemeId=${id}|name=${name}|status=${delResp.status}|body=${delResp.body}")
}
}
}
logger.info("=== SUMMARY ===")
logger.info("Total screen schemes: ${allScreenSchemes.size()}")
logger.info("Kept (referenced): ${keptReferenced}")
logger.info("Kept (protected by rules): ${keptProtected}")
logger.info("Delete candidates: ${candidates.size()}")
candidates.each { c ->
logger.info("CANDIDATE|screenSchemeId=${c[0]}|name=${c[1]}|reason=${c[2]}")
}
logger.info("=== DONE ===")