The following scripts and instructions are provided as-is, no warranties attached; use with care. Please feel free to adapt them to your needs. Note: We don't provide support for these apps; if you have doubts concerning its usage, please contact Cprime support. |
Cprime provides a set of Jira apps that allow the implementation of powerful custom automation rules, including Power Scripts, Power Custom Fields for Jira, and Power Actions for Jira.
All of those come with the SIL (Simple Issue Language) engine that provides an abstraction layer over the internal of Atlassian APIs, allowing you to easily implement and maintain Jira automation-related scripts.
Power Scripts | Power Custom Fields for Jira | Power Actions for Jira | |
---|---|---|---|
used to... | implement automation rules | implement custom fields whose values are derived from custom logic | implement buttons/UI elements, having a custom logic |
triggering |
|
| N/A |
This use case requires the Power Scripts Fields app.
Sometimes you may need to create a Test Set, a Test Execution, or a Test Plan programmatically.
The following example shows how to create a Test Execution; it can easily be adapted for other Xray issue types. You'll need to update the corresponding REST API endpoint for adding the tests to that entity.
//Define struct for HTTP request struct change { string [] add; string [] remove; } string jiraBaseUrl = getJIRABaseUrl(); // string jiraBaseUrl = "https://yourjiraserver.example.com"; string jiraUsername = "someuser"; string jiraPassword = "somepass"; function addTestsToTestExecution(string testExecKey, string[] testList){ //Create array/struct change addRequest; //Add data to array/struct addRequest.add = testList; string endpointUrl = #{jiraBaseUrl} + "/rest/raven/1.0/api/testexec/" + #{testExecKey} + "/test"; HttpRequest request; HttpHeader authHeader = httpBasicAuthHeader(jiraUsername, jiraPassword); request.headers += authHeader; HttpHeader header = httpCreateHeader("Content-Type", "application/json"); request.headers += header; //Post data and get response string result = httpPost(endpointUrl, request, addRequest); logPrint("DEBUG", "result: " + #{result}); return true; } string projectKey = "BOOK"; string parentIssueKey = ""; string issueType = "Test Execution"; string issueSummary = "test execution created automatically from a SIL script"; string testExecKey = createIssue(projectKey, parentIssueKey, issueType, issueSummary); logPrint("DEBUG", "On the project " + projectKey + ", issue " + testExecKey + " was created."); string [] testList = { "BOOK-14", "BOOK-15" }; // string jql = "project = BOOK and issuetype = Test"; // string [] testList = selectIssues(jql); addTestsToTestExecution(testExecKey, testList); |
Please check SIL's documentation for more info on createissue() and on httpPost().
This use case requires the Power Scripts Fields app.
Sometimes you may need to assure that the requirement is actually OK before transitioning it to some status, or before resolving it.
The following script validates the requirement based on the tests executed for the version assigned to the requirement issue.
You can either make the validation based on the existence of failed tests (i.e. requirement status is "NOK") or, in a more complete way by making sure all of them are passing (i.e. requirement status is "OK").
string version = join(fixVersions,""); string jql = "key = "+ #{key} + " and issue in requirements('OK','" + #{project} + "','"+#{version} + "')"; logPrint("DEBUG",jql); int numberOfIssues = countIssues(jql); if (numberOfIssues != 1) { return false, "Requirement Status", "Some tests need to be executed. You must assure that they pass before making the transition."; } return true; |
This use case requires the Power Scripts Fields app.
Whenever you change the specification of a requirement/user story, you most probably will need to review the Tests that you have already specified.
The following script tries to make a transition on all linked Tests to a requirement. You can hook it to a post-function on some transition of the requirement/user story.
Please check SIL's documentation for more info on autotransition().
string transition = "Reopen Issue" string jql = "issue in requirementTests('" + #{key} + "')"; string [] keys = selectIssues(jql); for(string testKey in keys) { logPrint("DEBUG","test_key: " + #{testKey}); autotransition(transition, testKey); } |
The following examples show how you can implement some actions, using Power Actions, to trigger a build/job in a CI/CD tool such as Jenkins, Bamboo, or others.
There are some come common steps:
sil.properties
JENKINS_BASE_URL=http://myjenkins.example.com JENKINS_USERNAME=admin JENKINS_PASSWORD=fa02840152aa2e4da3d8db933ec708d6 JENKINS_TOKEN=iFBDOBhNhaxL4T9ass93HRXun2JF161Z BAMBOO_BASE_URL=http://mybamboo.example.com BAMBOO_USERNAME=admin BAMBOO_PASSWORD=admin |
In this case, we're going to give users the ability to trigger an existing job/project in Jenkins.
We'll obtain the list of available jobs/projects using Jenkins REST API which we'll present to the user. Then, we trigger the build after the user confirms some options.
In Jenkins, we need to generate an API token for some user, which can be done from the profile settings page.
At the project level, we need to enable remote build triggers, so we can obtain an "authentication token" to be used in the HTTP request afterwards.
The project itself is a normal one; the only thing relevant to mention is that this project is a parameterized one, so it receives a TESTPLAN variable, that in our case will be coming from Jira.
The final task submits the results linking the Test Execution to the Test Plan passed as an argument.
We need to configure the Power Action custom field created earlier, namely the Condition, Screen, and Action Scripts; this from the custom field configuration as follows.
number ENABLED = 1; number DISABLED = 2; number HIDDEN = 3; return ENABLED; |
struct job { string name; } struct answer { job [] jobs; } persistent string defaultJobName = ""; string jenkinsBaseUrl = silEnv("JENKINS_BASE_URL"); string jenkinsUsername = silEnv("JENKINS_USERNAME"); string jenkinsPassword = silEnv("JENKINS_PASSWORD"); function getJenkinsJobs(){ string requestURL = #{jenkinsBaseUrl} + "/api/json"; HttpRequest request; HttpHeader authHeader = httpBasicAuthHeader(jenkinsUsername, jenkinsPassword); request.headers += authHeader; HttpHeader header = httpCreateHeader("Content-Type", "application/json"); request.headers += header; //Post data and get response answer ans = httpPost(requestURL, request); logPrint("DEBUG", "answer: " + #{ans}); return ans.jobs; } boolean isDisabled = false; string [] jobs = getJenkinsJobs(); string [] ret = BA_setActionTitle("Trigger CI/CD build" + defaultJobName); ret = BA_createSelectList("job", jobs, defaultJobName, isDisabled); ret = arraysConcat(ret, BA_createSingleCheckbox("report to this test plan", true, isDisabled)); ret = arraysConcat(ret, BA_createSingleCheckbox("set as default job", false, isDisabled)); return ret; |
struct job { string name; } struct answer { job [] jobs; } string jenkinsBaseUrl = silEnv("JENKINS_BASE_URL"); string jenkinsUsername = silEnv("JENKINS_USERNAME"); string jenkinsPassword = silEnv("JENKINS_PASSWORD"); string token = silEnv("JENKINS_TOKEN"); //string jobName = "java-junit-calc"; string jobLabel = getElement(argv, 0); string jobName = getElement(argv, 1); string reportToTestPlanLabel = getElement(argv, 2); boolean reportToTestPlan = BA_isChecked(argv, reportToTestPlanLabel); //getElement(argv, 3); string setAsDefaultJobLabel = getElement(argv, 4); boolean setAsDefaultJob = BA_isChecked(argv, setAsDefaultJobLabel); //getElement(argv, 5); persistent string defaultJobName = ""; function triggerJenkinsBuild(string testPlanKey, string jobName){ string requestURL = #{jenkinsBaseUrl} + "/job/" + #{jobName} + "/buildWithParameters?token=" + #{token} + "&TESTPLAN=" + #{testPlanKey}; HttpRequest request; HttpHeader authHeader = httpBasicAuthHeader(jenkinsUsername, jenkinsPassword); request.headers += authHeader; HttpHeader header = httpCreateHeader("Content-Type", "application/json"); request.headers += header; //Post data and get response string result = httpPost(requestURL, request); logPrint("DEBUG", "result: " + #{result}); return true; } if (setAsDefaultJob){ defaultJobName = jobName; } if (reportToTestPlan) { triggerJenkinsBuild(key, jobName); } else { triggerJenkinsBuild("", jobName); } |
A new button will be available on the custom field. You'll need to add it to the view issue screen.
From the "job" dropdown you can select the job you want to run. You can also specify whether you want to report the results back to the current Test Plan and if you want to set that job as the default one for that Test Plan (so it pre-selected the next time you invoke it).
In this case, we're going to give users the ability to trigger an existing job/project in Jenkins.
We'll obtain the list of available plans using Bamboo REST API which we'll present to the user. Then, we trigger the build after the user confirms some options.
The project itself is a normal one; the only thing relevant to mention is that this project is a parameterized one, so it receives a TESTPLAN variable, that in our case will be coming from Jira.
The final task submits the results linking the Test Execution to the Test Plan passed as an argument.
We need to configure the Power Action custom field created earlier, namely the Condition, Screen, and Action Scripts; this from the custom field configuration as follows.
number ENABLED = 1; number DISABLED = 2; number HIDDEN = 3; return ENABLED; |
struct plan { string name; string key; } struct plans { plan [] plan; } struct answer { plans plans; } string bambooBaseUrl = silEnv("BAMBOO_BASE_URL"); string bambooUsername = silEnv("BAMBOO_USERNAME"); string bambooPassword = silEnv("BAMBOO_PASSWORD"); persistent string defaultBambooPlanName = ""; function getBambooPlans(){ string requestURL = #{bambooBaseUrl} + "/rest/api/latest/plan.json"; HttpRequest request; HttpHeader authHeader = httpBasicAuthHeader(bambooUsername, bambooUsername); request.headers += authHeader; HttpHeader header = httpCreateHeader("Content-Type", "application/json"); request.headers += header; //Post data and get response answer ans = httpGet(requestURL, request); logPrint("DEBUG", "answer: " + #{ans}); string [] jobs; for(plan plan in ans.plans.plan) { logPrint("DEBUG","plan_key: " + plan.key); jobs = addElement(jobs, plan.key); } return jobs; } boolean isDisabled = false; string [] jobs = getBambooPlans(); string [] ret = BA_setActionTitle("Trigger CI/CD build"+defaultBambooPlanName); ret = BA_createSelectList("plan", jobs, defaultBambooPlanName, isDisabled); ret = arraysConcat(ret, BA_createSingleCheckbox("report to this test plan", true, isDisabled)); ret = arraysConcat(ret, BA_createSingleCheckbox("set as default plan", false, isDisabled)); return ret; |
string bambooBaseUrl = silEnv("BAMBOO_BASE_URL"); string bambooUsername = silEnv("BAMBOO_USERNAME"); string bambooPassword = silEnv("BAMBOO_PASSWORD"); string planLabel = getElement(argv, 0); string planName = getElement(argv, 1); string reportToTestPlanLabel = getElement(argv, 2); boolean reportToTestPlan = BA_isChecked(argv, reportToTestPlanLabel); //getElement(argv, 3); string setPlanAsDefaultLabel = getElement(argv, 4); boolean setPlanAsDefault = BA_isChecked(argv, setPlanAsDefaultLabel); //getElement(argv, 5); persistent string defaultBambooPlanName = ""; function triggerBambooBuild(string testPlanKey, string planName) { string requestURL = #{bambooBaseUrl} + "/rest/api/latest/queue/" + #{planName}; // "default&ExecuteAllStages=true&bamboo.TESTPLAN=#{testPlanKey}" HttpRequest request; HttpHeader authHeader = httpBasicAuthHeader(bambooUsername, bambooPassword); request.headers += authHeader; HttpHeader header = httpCreateHeader("Content-Type", "application/x-www-form-urlencoded"); request.headers += header; request.parameters += httpCreateParameter("default", "true"); request.parameters += httpCreateParameter("ExecuteAllStages", "true"); request.parameters += httpCreateParameter("bamboo.TESTPLAN", testPlanKey); //Post data and get response string result = httpPost(requestURL, request); logPrint("DEBUG", "result: " + #{result}); return true; } if (setPlanAsDefault) { defaultBambooPlanName = planName; } if (reportToTestPlan) { triggerBambooBuild(key, planName); } else { triggerBambooBuild("", planName); } |
A new button will be available on the custom field. You'll need to add it to the view issue screen.
From the "plan" dropdown you can select the plan you want to run. You can also specify whether you want to report the results back to the current Test Plan and if you want to set that job as the default one for that Test Plan (so it pre-selected the next time you invoke it).
The idea is to display information about the number of passed, failed, etc tests as text, on Test Execution issues using a specific custom field.
Therefore, we'll use the Power Custom Fields app.
Now you can add this custom field on the Test Execution view issue screen if you want. In this specific case, it would be a bit redundant though, as the progress bar bellow shows those values.
You can also add it as a column on panels that show Test Executions (e.g. on the Test Plan).