Sidehistorik
...
- Disable mail
- Limit access
- Install Apps - Notice this is not like Data Center - You cant disable and enable them as You please....
Clean up
I do recommend to clean up first:
...
A test - Migrate a project - Delete it - Migrate it again... Gave a lot of (migrated) fields, screens etc etc
| Advarsel |
|---|
Current recommendation seems to be a sire Reset (for Jira and Jira SErvice Management, and then a Big Bang migrate. |
Dark Features
Disable Workflow migration errors
...
| Kodeblok | ||||
|---|---|---|---|---|
| ||||
PASSWORD=******************* |
...
| Kodeblok | ||||
|---|---|---|---|---|
| ||||
#!/bin/bash source automationpasswordpassword.txt IFS=$(echo -en ",") BASEURL="https://jira.server.dk" REST="/rest/scriptrunner/latest/custom/UpdateShadowFieldsForIssue" USERNAME=automation DRYRUN="1" EMPTYONLY="1" SKIPINDEX="" DAYSBACK= #Loop projects curl -s -u "$USERNAME:$PASSWORD" -X GET -H 'Content-Type: application/json' https://jira.server.dk$BASEURL/rest/api/2/project?maxResults=1000 > projects.json cat projects.json | jq '.[].key' | while read -r PROJECTKEY; do PROJECTKEY=$(echo $PROJECTKEY | sed 's/\"//g') #echo "$PROJECTKEY" #Loop Issuetypes for the Project curl -s -u "$USERNAME:$PASSWORD" -X GET -H 'Content-Type: application/json' https://jira.server.dk/$BASEURL/rest/api/2/issuetype >/issue/createmeta/$PROJECTKEY/issuetypes?maxResults=1000 > issuetypes.json cat issuetypes.json | jq '.values[].name' | while read -r ISSUETYPE; do ISSUETYPEISSUETYPE=$(echo $ISSUETYPE | sed 's/\"//g') #echo $ISSUETYPE #Loop Fields declare -a FIELDMAP=("23321|29033|M" "23523|29020|S" "24523|29021|S" "23522|29022|S" "24623|29026|S" "24422|29023|S" "23726|29024|S" "23728|29034|M" "23729|29035|M" "23727|29025|S" "24920|29036|S" "24921|29037|S" "24624|29027|S" "24423|29028|S" "23320|29032|S" "24821|29031|S" "24625|29030|S" "24424|29029|S" "24922|29039|S" "24923|29038|S" "24924|29040|S" "24925|29041|S" "24926|29042|S" "25028|29043|S" "25027|29044|S") for FIELD in "${FIELDMAP[@]}" do #echo "Field $FIELD" ELEMENTSFIELD SOURCEFIELDID=$(echo $FIELD | cut -d '|' -f 1) SHADOWFIELD SHADOWFIELDID=$(echo $FIELD | cut -d '|' -f 2) TYPE=$(echo $FIELD | cut -d '|' -f 3) ISSUETYPE=$(echo $ISSUETYPE | sed 's/ /%20/g') #Sample: https://jira.server.dk/rest/scriptrunner/latest/custom/UpdateShadowFieldsForIssue?projectkey=NIS&issuetype=Repeatable%20Task&elementsfield=23321&shadowfield=29033&type=M&emptyonly=1&dryrun=0&skipindex=&daysback=10 QUERYSTRING="projectkey=$PROJECTKEY& QUERYSTRING="projectkey=$PROJECTKEY&issuetype=$ISSUETYPE&elementsfieldsourcefieldid=$ELEMENTSFIELD$SOURCEFIELDID&shadowfieldshadowfieldid=$SHADOWFIELD$SHADOWFIELDID&type=$TYPE&emptyonly=$EMPTYONLY&dryrun=$DRYRUN&skipindex=$SKIPINDEX&daysback=$DAYSBACK" echo "curling.... $QUERYSTRING" curl -u curl -u "$USERNAME:$PASSWORD" -X GET -H 'Content-Type: application/json' "$BASEURL$REST?$QUERYSTRING" done done done |
The scriptrunner REST code:
...
| Kodeblok | ||||
|---|---|---|---|---|
| ||||
elementsFieldValue = extractValues(theIssue.getCustomFieldValue(elementsField),type) |
| Kodeblok | ||
|---|---|---|
| ||
elementsFieldValue = theIssue.getCustomFieldValue(elementsField) |
For normal copy
| language | shell |
|---|---|
| title | UpdateShadowFieldsForIssue.groovy |
| title | UpdateShadowFieldsForIssue.groovy |
|---|
import com.atlassian.import com.atlassian.jira.component.ComponentAccessor import com.atlassian.jira.issue.Issue import com.atlassian.jira.issue.IssueManager import com.atlassian.jira.user.util.UserUtil import com.atlassian.jira.issue.CustomFieldManager import com.atlassian.jira.issue.fields.CustomField import com.atlassian.jira.bc.issue.search.SearchService import com.atlassian.jira.issue.search.SearchProvider import com.atlassian.jira.issue.search.SearchResults import com.atlassian.jira.web.bean.PagerFilter import com.atlassian.jira.user.ApplicationUser import com.atlassian.jira.event.type.EventDispatchOption import groovy.json.JsonSlurper import com.atlassian.jira.issue.index.IssueIndexingService import com.atlassian.jira.issue.MutableIssue import com.atlassian.jira.issue.IssueManager import com.atlassian.jira.util.ImportUtils import groovy.transform.BaseScript import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate import javax.ws.rs.core.MultivaluedMap import javax.servlet.http.HttpServletRequest import javax.ws.rs.core.Response @BaseScript CustomEndpointDelegate delegate UpdateShadowFieldsForIssue(httpMethod: "GET", groups: ["automaticservices","jira-administrators"]) { MultivaluedMap queryParams -> Random random = new Random() String scriptRunIdent = Math.abs(random.nextInt() % 99999) + 1 String scriptName = "UpdateShadowFieldsForIssue.groovy" String projectKey = queryParams.getFirst("projectkey") as String String issueType = queryParams.getFirst("issuetype") as String String elementsFieldIdsourceFieldId = queryParams.getFirst("elementsfieldsourcefieldid") as String String shadowFieldId = queryParams.getFirst("shadowfieldshadowfieldid") as String String type = queryParams.getFirst("type") as String String emptyOnly = queryParams.getFirst("emptyonly") as String String dryRun = queryParams.getFirst("dryrun") as String String skipIndex = queryParams.getFirst("skipindex") as String String daysBack = queryParams.getFirst("daysback") as String Integer currentNumber = 0 def builder = new groovy.json.JsonBuilder() String jql="Project=" + projectKey + " and issuetype='" + issueType + "' and cf[" + elementsFieldIdsourceFieldId + "] is not empty" if (emptyOnly == "1") { jql=jql + " and cf[" + shadowFieldId + "] is empty" } if (daysBack != "") { jql=jql + " and updated > -" + daysBack + "d" } log.info "Script=" + scriptName + " ScriptRunIdent=" + scriptRunIdent + " Message='JQL: " + jql + "'" ApplicationUser currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser() CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager() IssueManager issueManager = ComponentAccessor.getIssueManager() def issueIndexingService = ComponentAccessor.getComponent(IssueIndexingService) CustomField elementsFieldsourceField = customFieldManager.getCustomFieldObject("customfield_" + elementsFieldIdsourceFieldId) String jiraSourceFieldType =sourceField.getCustomFieldType().getName() CustomField shadowField = customFieldManager.getCustomFieldObject("customfield_" + shadowFieldId) String jiraFieldTypejiraShadowFieldType = shadowField.getCustomFieldType().getName() loglog.info "Script=" + scriptName + " ScriptRunIdent=" + scriptRunIdent + " Message='ShadowSource field Jira type: " + jiraFieldTypejiraSourceFieldType + "'" //Make sure we copy to a text field. if (jiraFieldTypelog.info "Script=" + scriptName + " ScriptRunIdent=" + scriptRunIdent + " Message='Shadow field Jira type: " + jiraShadowFieldType + "'" if (jiraShadowFieldType == "Text Field (single line)" || jiraFieldTypejiraShadowFieldType == "Text Field (multi-line)") { SearchService searchService = ComponentAccessor.getComponent(SearchService.class) SearchService.ParseResult parseResult = searchService.parseQuery(currentUser, jql) if (parseResult.isValid()) { SearchResults results = searchService.search(currentUser, parseResult.getQuery(), PagerFilter.getUnlimitedFilter()) final List issues = results?.results totalIssues = issues.size() issues.each { theIssue -> log.info "Script=" + scriptName + " ScriptRunIdent=" + scriptRunIdent + " Message='Value " + theIssue.getCustomFieldValue(sourceField) + "'" //Extract value(s) here from Source field. Its possible to expand for another 'jiraSourceFieldType' if (jiraSourceFieldType.contains("Elements Connect")) { sourceFieldValue = extractElementsValues(theIssue.getCustomFieldValue(sourceField),type) SearchResults results = searchService.search(currentUser, parseResult.getQuery(), PagerFilter.getUnlimitedFilter()) } final List issues = results?.results totalIssues = issues.size() else issues.each { theIssue -> { log.info "Script=" + scriptName + " ScriptRunIdent=" + scriptRunIdent + " Message='Value " + sourceFieldValue = theIssue.getCustomFieldValue(elementsFieldsourceField) + "'" elementsFieldValue = extractValues(theIssue.getCustomFieldValue(elementsField),type) } shadowFieldsValue = theIssue.getCustomFieldValue(shadowField) if (elementsFieldValuesourceFieldValue != shadowFieldsValue) { currentNumber = currentNumber + 1 if (dryRun != "1") { log.info "Script=" + scriptName + " IssueKey=" + theIssue.getKey() + " ScriptRunIdent=" + scriptRunIdent + " Message='Copying " + elementsFieldIdsourceFieldId + " to " + shadowFieldId + ", value: " + elementsFieldValuesourceFieldValue + "'" MutableIssue mIssue = issueManager.getIssueByCurrentKey(theIssue.getKey()) try { mIssue.setCustomFieldValue(shadowField,elementsFieldValuesourceFieldValue) ComponentAccessor.getIssueManager().updateIssue(currentUser, mIssue, EventDispatchOption.DO_NOT_DISPATCH, false) } catch (Exception ex) { log.info "Script=" + scriptName + " IssueKey=" + theIssue.getKey() + " ScriptRunIdent=" + scriptRunIdent + " Error='Error Updating: " + ex.getMessage() + "'" } //Reindex Issue if (skipIndex != "1") { boolean wasIndexing = ImportUtils.isIndexIssues() ImportUtils.setIndexIssues(true) issueIndexingService.reIndex(mIssue) ImportUtils.setIndexIssues(wasIndexing) } } else { log.info "Script=" + scriptName + " IssueKey=" + theIssue.getKey() + " ScriptRunIdent=" + scriptRunIdent + " Message='DryRun - Copying " + elementsFieldIdsourceFieldId + " to " + shadowFieldId + ", value: " + elementsFieldValuesourceFieldValue + "'" } } else { log.info "Script=" + scriptName + " IssueKey=" + theIssue.getKey() + " ScriptRunIdent=" + scriptRunIdent + " Message='Skipped copy due to identical values'" } } if (dryRun != "1") { if (currentNumber > 0) { builder {success "Fields copied successfully. Copied " + currentNumber + " field(s)."} } else { builder {success "No Fields copied."} } } else { builder {success "Dry Run. Woulf have copied " + currentNumber + " field(s)."} } Response.status(200).entity(builder.toString()).build() } else { builder {error "Not valid JQL"} Response.status(500).entity(builder.toString()).build() } } else { builder {error "Shadow field is not a Text Field."} Response.status(500).entity(builder.toString()).build() } } def extractValuesextractElementsValues(theValue,fieldType) { if (theValue == null) { return null } def slurper = new JsonSlurper().parseText(theValue) if (fieldType == "M") { slurper.keys.join("\n").toString() } else { slurper.keys[0].toString() } }
...