Affichage des articles dont le libellé est JIRA. Afficher tous les articles
Affichage des articles dont le libellé est JIRA. Afficher tous les articles

lundi 31 août 2020

Rsync and exclusions

 

an old post found back on disqr about rsync (original date : circa 2016)

You might want to use the rsync exclude options directly in your script either by specifying a file with all the exclusions to perform, or by specifying them in the command line directly :

--exclude-from <file-name with 1 pattern by line>
--exclude <file or="" dir="">

For example :

rsync -r -a -v -e "ssh -l Username" \
        --exclude 'dbconfig.xml' --exclude 'WEB-INF/classes/' --delete \
        /local/Directory remote.server:/remoteDirectory

One important thing to keep in mind when excluding a directory is that rsync will always consider the path to be relative to the source directory.



This can be used for example when you want to push your production from a Production JIRA instance to a Staging JIRA instance, but your dbconfig.xml is different (different DB auth parameters for example), and hence want to avoid some files. 

vendredi 10 janvier 2020

JIRA + Script Runner : template / default Description value

using Adaptavist Script Runner plugin > Behaviour


def desc = getFieldById ("description")

def defaultValue = """ *Some text in bold*
since we have a multiline string, we can put some more
but keep it short, this is a template...
What / Why / How ...""".replaceAll(/ .   /, '')

if (! underlyingIssue.descrption) {
        desc.setFormValue(defaultValue)
}

mardi 1 octobre 2019

JIRA - find a plugin usage in a Workflow (groovy Script Runner + SQL version)

 Know where/if  a plugin is used. Both have to be adapted to match a specific plugin.

First version gives more data but requires ScriptRunner.

Second version only uses SQL, but is less extensive.


You need to know what you're looking for : com.innovalog.jmwe.jira-misc-workflow-extensions


Script Groovy

Adapted from :  https://answers.atlassian.com/questions/205094/how-to-find-all-workflows-which-are-using-postfunctions-of-a-plugin

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.workflow.JiraWorkflow
import com.atlassian.jira.workflow.WorkflowManager
import java.util.regex.Matcher
  
String searchText = 'com.innovalog.jmwe.jira-misc-workflow-extensions'
  
WorkflowManager workflowManager = ComponentAccessor.getWorkflowManager()
Collection<JiraWorkflow> workflows = workflowManager.getWorkflows()
  
String result = "Workflow;Function;Type;Action;Step\r\n<br/>"
  
workflows.each{workflow ->
    def workflowXml = workflow.descriptor.asXML()
    Matcher m = workflowXml =~ /${searchText}/
    if (m.find()) {
        String workflow_name = "${workflow.name}${if(!workflow.isActive()){";(inactive)"} else ';active'}${if(workflow.isDraftWorkflow()){"(draft)"} else ''}"
        def wf = new XmlParser().parseText(workflowXml)
        List<Node> wf_flat = wf.depthFirst()
        wf_flat.each{
            if (it.text() ==~ /.*${searchText}.*/){
                result += "$workflow_name;${getNodeInfo(it,searchText)}\n\n<br/>"
            }
        }
    }
}
result
  
String getNodeInfo(Node n, String search) {
    String nodetext = ''
    nodetext += "${n.text() - search}"
    def p = n.parent()
    while (p) {
        switch (p.name()) {
            case '"post-functions"':
                nodetext += ";post-function "; break
            case 'validators':
                nodetext += ";validator "; break
            case 'conditions':
                nodetext += ";condition "; break
            case 'action':
                nodetext += ";${p.attribute('name')} (${p.attribute('id')})"; break
            case 'step':
                nodetext += ";${p.attribute('name')} (${p.attribute('id')})"; break
            case 'global-actions':
                nodetext += ";GLOBAL "; break
            case 'initial-actions':
                nodetext += ";INITIAL "; break
  
        }
        p = p.parent()
    }
    return nodetext
}

 

SQL Query

SELECT
  workflowscheme.name,
  workflowschemeentity.scheme,
  issuetype.pname,
  workflowschemeentity.issuetype
FROM
  issuetype,
  workflowschemeentity,
  workflowscheme
WHERE
issuetype.id = workflowschemeentity.issuetype and
workflowscheme.id = workflowschemeentity.scheme and
  workflowschemeentity.workflow in
    (SELECT
        jiraworkflows.workflowname
    FROM
        jiraworkflows
    WHERE
        jiraworkflows.descriptor like '%com.company.plugin%');

mercredi 21 août 2019

JIRA Python script : iterate to get the JQL result

Python 2.7 script to :

  • query the result of a JIRA search query in JQL
  • iterate so that we do not get all the data at once but only by small batches
  • print the result list

Pre-requesites :

  • python
  • https://pypi.org/project/atlassian-python-api/
  • https://atlassian-python-api.readthedocs.io/en/latest/index.html
  • some JIRA Server or JIRA Cloud + credentials
    • Note that the auth for JIRA Cloud might change a bit



from atlassian import Jira
import json

#auth for JIRA Server
jira = Jira(
    url='https://support.valiantys.com',
    username='username',
    password='password')
# Get the SERVER issues that are Running
JQL = 'project = DEMO AND resolution = Unresolved AND issuetype = "Story" AND status = "In Progress" ORDER BY cf[16032] DESC, cf[15857] DESC'

data_tmp = {}   # data we progressively get each interation
issues_all = [] # full list of issues
issues_tmp = {} # list of issues during this iteration

next_start = 0
size = 50

while size > 0 :
        print("-------- trying to get issues:%s..%s" % (next_start, next_start+size))
        data_tmp = jira.jql(JQL, start=next_start, limit=size)
        next_start += size
        issues_tmp = data_tmp['issues']
        size = len(data_tmp['issues'])
        issues_all.extend(issues_tmp)

print ("(final) #issues=%s" % len(issues_all))

# now we can do things with the data
for x in issues_all:
print("%s %s %s %s" % (x['key'], x['fields']['customfield_16032'], x['fields']['customfield_15862'], x['fields']['customfield_15857']))

vendredi 7 juin 2019

JIRA Script Runner log debug

snippet of code to use the output logs in ScriptRunner Adaptavist groovy scripts  

ScriptRunner log.debug

tool

ScriptRunner for JIRA

version

8.1.1

use case
log.debug SR script


/***********/
import org.apache.log4j.Logger
import org.apache.log4j.Level
 
def log = Logger.getLogger("com.scriptname")
log.setLevel(Level.DEBUG)
/***********/
 
//and then, for example to print the variable trem
log.debug "trem=${trem}"


example : 



mercredi 7 juin 2017

JIRA Xporter plugin - tips and tricks

Tips for  the plugin Xporter 

plugin : https://marketplace.atlassian.com/apps/891368/xporter-export-issues-from-jira?hosting=server&tab=overview

  • Print the current date
  • Count number of issues in a JQL
  • Sum a CF for all issues on a JQL
  • Number of links
  • Number of issues in a JQL (number format)
  • Format a number with locale's decimal separator
  • SUM Excel correct values in wrong locale format (workaround)
  • Iteration on issue & subtasks


Print the current date

toolXporter
version5.0.0
use casePrint the current date


Document generated at
%{(new Date()).getDate() + "/" + ((new Date()).getMonth()+1) +
"/" + (new Date()).getFullYear()}

Count number of issues in a JQL

toolXporter, excel
version5.0.0
use caseCount number of issues in a JQL

Excel

JQL<filter>
nfiches1${jqlcount:
nfiches2}

usage

=CONCATENATE(nfiches1;<JQL>;nfiches2)

example

${jqlcount:project = PJAB AND issuetype = Pénal AND created >= -90d AND status not in ("Constitution du dossier", "Saisir Avocat") AND Entité = "AXA Banque"}


Sum a CF for all issues on a JQL

toolXporter, excel
version5.0.0
use caseSum a CF for all issues on a JQL

Excel

JQL<filter>
CF<cf name>
nsomme1${set(count,0)} #{for n=JQLIssuesCount|clause=
nsomme2}
#{if (%{'${JQLIssues[n].
nsomme3}' .length > 0})}
${set(count,%{${count} + ${JQLIssues[n].
nsomme4}})}
#{end}
#{end}
%{Number(${count}).toFixed(2)}

usage

in a excel cell :

=CONCATENATE(nsomme1;<JQL>;nsomme2;<CFname>;nsomme3;<CFname>;nsomme4)


example

${set(count,0)} #{for n=JQLIssuesCount|clause=project = PJAB AND
issuetype = Pénal AND created >= -90d AND status not in
("Constitution du dossier", "Saisir Avocat") AND Entité = "AXA Banque"}
 #{if (%{'${JQLIssues[n].Créance comptable}' .length > 0})}
 ${set(count,%{${count} + ${JQLIssues[n].Créance comptable}})}
 #{end}
 #{end}
 %{Number(${count}).toFixed(2)}

Number of links

toolXporter, excel
version5.0.1
use caseNumber of links matching a criteria (including creation date)


  • Count the links :
    This requires to set a counter, and iterate over the links returned by "LinksCount".
  • Filter on a date :
    This requires to create different Date objects, either initiated with the date returned from the issue, or the calculated date. In the end we are indeed comparing milliseconds since 1-1-1970, so the > is enough.

    /*  test if the creation date is within the last 90 days */
    (new Date('${dateformat("yyyy-MM-dd HH:mm:ss"):Links[n].Created}') > (new Date(new Date().setDate(new Date().getDate()-90))))

example

/* Set the variable countD4 to the number of links to the current issue "A" where :
* link with the current is :  "A" -(est modifié par)-> other issue
* linked issue was creted less than 90 days ago
* linked issue type is "Créa-Modif Document".
*/
${set(countD4,0)}
#{for n=LinksCount|filter=%{('${Links[n].LinkType}'.equals('est modifié par')) && (new Date('${dateformat("yyyy-MM-dd HH:mm:ss"):Links[n].Created}') > (new Date(new Date().setDate(new Date().getDate()-90)))) && '${Links[n].IssueTypeName}'.equals('Créa-Modif Document')}}
${set(countD4,%{${countD4}+1})}
    * ${Links[n].Key} ${Links[n].Summary} ${Links[n].IssueTypeName}  ${Links[n].Status}
#{end}
${countD4}



Number of issues in a JQL (number format)

toolXporter, excel
version5.0.1
use caseCount the number of issues, and print it in excel JQL format
  • ${jqlcount:<JQL>} : counts the number of issues returned by the JQL
  • %{Number(<...>)} : make sure it's rendered as a number in excel, for example to use it with =SUM() excel function

example

%{Number(${jqlcount:project = PJAB AND issuetype = Pénal AND created >= -92d AND status not in ("Constitution du dossier", "Saisir Avocat") AND Entité = "AXA Banque"})}

Format a number with locale's decimal separator

toolXporter
version?
use caseFormat a number with specific locale's decimal separator
  • use the ${numberformat("fr","#,##0.00#") function

example

${numberformat("fr","#,##0.00#"):JQLIssues[j].Montant de l'opération (en €)}

SUM Excel correct values in wrong locale format (workaround)

toolExcel, Xporter
version5.1.1
use caseSum values that Xporter printed in the wrong locale.
  • Sometime Xporter has some trouble printing numbers that are actually recognized as numbers by excel. An example is by

example

let's say you need to sum values in a locale where the decimal separator is ",", but xporter outputs a decimal separator as "."; hence the =SUM() function will not work.

=SUM(VALUE(CLEAN(SUBSTITUTE(C11;".";",")));VALUE(CLEAN(SUBSTITUTE(C12;".";",")));VALUE(CLEAN(SUBSTITUTE(C13;".";","))))

Iteration on issue & subtasks

toolExcel, Xporter, ScriptRunner
version5.1.1
use caseBulk export, export the issues & insert their subtasks
  • One way of doing so is to use the ScriptRunner JQL functions "subtasksOf(<key>)" and iterate on it.

example



#{for j=JQLIssuesCount|clause=project = PRF and issueFunction in subtasksOf("key = ${Key}") and issuetype NOT IN ("Débit", "Crédit") order by updated }

lundi 13 février 2017

JIRA server tips and tricks : WF properties on Status

(note : this works on JIRA Server / Datacenter at the time of the writing of this, Feb 2017)


 Sometimes, you want to set some permissions directly depending on the JIRA Issue Workflow status (i.e. not depending on the whole Permissions scheme applied at the whole project level).

This hard to find trick consist in using Workflow properties on the WF steps.



Use-case : lock the "edit" button on 1 WF step, to 3 user groups

ref :

In order to allow the Edit right only to a specific user group, you need to add the following property to the workflow step impacted  so that only JIRA administrators can edit an issue
jira.permission.edit.group=jira-administrators


To add multiple groups, use the syntax :
jira.permission.edit.group.1=jira-administrators 
jira.permission.edit.group.2=jira-fr
jira.permission.edit.group.3=jira-pmo

 

This of course needs to be applied for each step/status of the workflow impacted, making sure that this WF is not shared with other projects or other IT.


JIRA : Status Permissions


Please note that the expected format is not <property> = denied This format actually grants access to the user named denied.
The proper format is is:
<property>.denied = whatever

The complete format is:

<permission key> = jira.permission[.subtasks].<system project permission>.<grant type>[.<suffix>]
 
<system project permission> =   assign
                                | assignable
                                | attach
                                | attachdeleteall
                                | attachdeleteown
                                | browse
                                | close
                                | comment
                                | commentdeleteall
                                | commentdeleteown
                                | commenteditall
                                | commenteditown
                                | create
                                | delete
                                | edit
                                | link
                                | managewatcherlist
                                | modifyreporter
                                | move
                                | project
                                | resolve
                                | scheduleissue
                                | setsecurity
                                | transition
                                | viewversioncontrol
                                | viewvotersandwatchers
                                | viewworkflowreadonly
                                | work
                                | worklogdeleteall
                                | worklogdeleteown
                                | worklogeditall
                                | worklogeditown
                                        
<grant type> =  denied
                | groupCF
                | assignee
                | assigneeassignable
                | reporter
                | reportercreate
                | userCF
                | applicationRole
                | group 
                | lead
                | projectrole 
                | user
 
<suffix> = any text that makes the permission key unique among all keys of permissions in the same workflow step.


 


lundi 30 janvier 2017

JIRA Script Runner / split cascading field in two text fields

JIRA 7.1.7
Script Runner 4.3.16

import com.atlassian.event.api.EventListener
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.event.issue.AbstractIssueEventListener
import com.atlassian.jira.event.issue.IssueEvent
import com.atlassian.jira.event.type.EventDispatchOption
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.CustomFieldManager
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.fields.CustomField
import org.apache.log4j.Logger
 
def cfNameCascading = "Cascading Field Name";
def cfNameFirst     = "Cascading Field Name : part 1";
def cfNameSecond    = "Cascading Field Name : part 2";
 
IssueManager issueManager = ComponentAccessor.getIssueManager()
CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager()
 
/** Get the different CF objects**/
CustomField cfCascading = customFieldManager.getCustomFieldObjectByName(cfNameCascading)
CustomField cfFirst = customFieldManager.getCustomFieldObjectByName(cfNameFirst)
CustomField cfSecond = customFieldManager.getCustomFieldObjectByName(cfNameSecond)
 
/** Get Cascading Field values (map) **/
Map cfVal = issue.getCustomFieldValue(cfCascading) as Map
if (cfVal) {
    String valFirst = cfVal.get(null);
    String valSecond = cfVal.get("1");
    List allValues = cfVal.values() as List;
 
    log.info("First - second: $valFirst - $valSecond");
    log.info("All: $allValues");
 
    /** Set each separate CF **/
    issue.setCustomFieldValue(cfFirst,  valFirst);
    issue.setCustomFieldValue(cfSecond, valSecond);
 
    /** update the issue **/
    issueManager.updateIssue(event.getUser(), issue, EventDispatchOption.DO_NOT_DISPATCH, false);
} else {
    log.info("Custom field not present on this issue")
}

vendredi 6 janvier 2017

JIRA Server - redirect to full issue view after creation

 (note : this works with most version of JIRA available as of now,  Janvier 2017)

Instead of staying in the current view, you can use the following tricks to make your JIRA SERVER / DATACENTER redirect directly to the recently created issue.


JavaScript (in banner)

The trick here is to disable the "popup" with the create screen, hence going to the "full create screen" which opens the page afterward.

<script type="text/javascript">
AJS.$("#create_link").removeClass("create-issue");
$("#announcement-banner").hide()</script>

cf. : https://confluence.atlassian.com/jirakb/how-to-disable-create-issue-popup-300813780.html

 

Plugin "Issue Quick Start" (and JS source)


The following plugin aims at the same :


Excerpt from the source :

AJS.$(document).on('DOMNodeInserted', function(event) {
    if (event.target.id == 'aui-flag-container') {
        console.log('issue-quick-start: Got post-it note!');
        AJS.$(event.target).on('DOMNodeInserted', function(event) {
            console.log('issue-quick-start: Post-it HTML: ' + event.target.innerHTML);
            var postItLink = AJS.$(event.target.innerHTML).find('a');
            var postItPath = postItLink.attr('href');
            if (postItPath && postItLink.attr('data-issue-key')) {
                console.log('issue-quick-start: Going to new issue path ' + postItPath);
                window.location = postItPath;
            }
        })
    }
});


[TODO] Script Runner : redirect with script fragment (??)

  • Is there some cleaner way to do it with SR "Script Fragments" to perform the redirect ?

cf. https://scriptrunner.adaptavist.com/4.3.7/jira/fragments/WebItem.html#_redirects

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.label.LabelManager
import com.atlassian.sal.api.ApplicationProperties
import com.onresolve.scriptrunner.runner.ScriptRunnerImpl
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.transform.BaseScript
 
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
 
@BaseScript CustomEndpointDelegate delegate
 
def labelManager = ComponentAccessor.getComponent(LabelManager)
def applicationProperties = ScriptRunnerImpl.getOsgiService(ApplicationProperties)
def issueManager = ComponentAccessor.getIssueManager()
 
labelIssue(httpMethod: "GET") { MultivaluedMap queryParams ->
 
    def issueId = queryParams.getFirst("issueId") as Long
    def issue = issueManager.getIssueObject(issueId)
 
/**    def label = labelManager.getLabels(issueId)
    if (! label) {
        labelManager.addLabel(null, issueId, "approved", false)
    }
**/
    Response.temporaryRedirect(URI.create("${applicationProperties.baseUrl}/browse/${issue.key}")).build()
}