jeudi 15 août 2019
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 :
mardi 9 avril 2019
Mac OS X : show (hidden) .files in the finder.
- CMD + Shift + .
- Open Terminal found in Finder > Applications > Utilities.
- In Terminal, paste the following:
defaults write com.apple.finder AppleShowAllFiles YES. - Press return.
- Hold the 'Option/alt' key, then right click on the Finder icon in the dock and click Relaunch.
vendredi 18 janvier 2019
Curl : get cert and connect using it
Obtain cert from self signed site
openssl s_client -showcerts -connect 75.74.58.21:443 <
/dev/null
2>
/dev/null
|
sed
-n
'/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p'
|
grep
-m1 -B-1 --
'-----END CERTIFICATE-----'
> cert.pem
Connect with cert
curl --cacert cert.pem https:
//hostname
.com
mercredi 1 août 2018
3D design tools for free
- Fusion360 : with an "enthusiast licence"
- Maya LT : used in the industry, there seems to be
- Blender : no, good for art but not for 3D modeling. Save revisions, save often, save each item so to be able to go to "stl repair" and import objects back. Boolean can kill objects.
- Sculptris for sculptures.
- Thinkercad.com : cool for beginners ?
- Shapr3D / only on an IpadPro ?
- Open Scad : for mechanical modeling or parametric designs ?
- SolidWorks ($$$)
- FreeCAD
mardi 7 novembre 2017
mercredi 7 juin 2017
JIRA Xporter plugin - tips and tricks
Tips for the plugin Xporter
- 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
tool | Xporter |
---|---|
version | 5.0.0 |
use case | Print the current date |
Document generated at %{( new Date()).getDate() + "/" + (( new Date()).getMonth()+ 1 ) + "/" + ( new Date()).getFullYear()} |
Count number of issues in a JQL
tool | Xporter, excel |
---|---|
version | 5.0.0 |
use case | Count 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
tool | Xporter, excel |
---|---|
version | 5.0.0 |
use case | Sum 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
tool | Xporter, excel |
---|---|
version | 5.0.1 |
use case | Number 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)
tool | Xporter, excel |
---|---|
version | 5.0.1 |
use case | Count 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
tool | Xporter |
---|---|
version | ? |
use case | Format 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)
tool | Excel, Xporter |
---|---|
version | 5.1.1 |
use case | Sum 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
tool | Excel, Xporter, ScriptRunner |
---|---|
version | 5.1.1 |
use case | Bulk 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 } |
vendredi 26 mai 2017
GMAIL - Google Script - auto delete old (junk) mail
Let's start with gmail since I have some needs I could not fit into the pretty basic filters that can be defined directly from the UI.
Context :
* I am able to identify labels are "search" (defined at the time the mail arrives) on my mailbox, that I want to delete after a certain period of time.
For instance, I might be interested in some promotions sent by some companies I have reductions cards with, but after X days, it's of no use and it's only taking precious space in my inbox.
* I do not wish to delete them directly, the "usual" trash with 30 days retention period is fine enought.
* I want to have this run every night.
1. Go to https://script.google.com
2. Enter this script :
- set the variable delayDays
- set the variable mySearch accordingly to your needs.
function cleanUp() {
/**
* @var delayDays int : Enter # of days before messages are moved to trash
* @var mySearch string : Enter the search as tested in Gmail search bar.
*/
var delayDays = 30 ;
var mySearch = 'label:online_store-totoStore OR from:bonsplans@newsletter.travel.com .com OR label:paris-freecycle';
//-- code
var maxDate = new Date();
maxDate.setDate(maxDate.getDate()-delayDays);
var tmp_threads = GmailApp.search(mySearch);
var threads = [];
var threads = threads.concat(tmp_threads);
for (var i = 0; i < threads.length; i++) {
if (threads[i].getLastMessageDate()
{
threads[i].moveToTrash();
}
}
}
EDIT 2020 : https://github.com/copolycube/GmailPurgeEmailsMatchingQuery
- array of simplier queries to find
- does multiple search instead of 1 massive one
- concatenates a "avoid deletion", for example to keep "starred" e-mail (by default)
3. Set it up to run every night :
3.1 : select the 'clock/trigger'.
3.2 : add the desired triggers on the popup window that will appear.
Other solution (easier)
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 :
- https://confluence.atlassian.com/adminjiraserver072/workflow-properties-828788054.html
- https://jirasupport.wordpress.com/2016/02/23/workflow-properties/
so
that only JIRA administrators can edit an issuejira.permission.edit.group=jira-administrators |
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
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"
)
}