I am trying to extract the username and password in jenkins groovy script who has initiated the build. I need these details to post comments on jira from my name.
So for eg.. I login into jenkins and start a job, then my login credentials should be used to post the comment on jira..
I tried alot of posts but didnt find anytihng related to my requirement.
Any help will be appreciated..
after few seconds of Googling, I found this script officially published by cloudbees.
So, as follows:
Jenkins.instance.getAllItems(Job).each{
def jobBuilds=it.getBuilds()
//for each of such jobs we can get all the builds (or you can limit the number at your convenience)
jobBuilds.each { build ->
def runningSince = groovy.time.TimeCategory.minus( new Date(), build.getTime() )
def currentStatus = build.buildStatusSummary.message
def cause = build.getCauses()[0] //we keep the first cause
//This is a simple case where we want to get information on the cause if the build was
//triggered by an user
def user = cause instanceof Cause.UserIdCause? cause.getUserId():""
//This is an easy way to show the information on screen but can be changed at convenience
println "Build: ${build} | Since: ${runningSince} | Status: ${currentStatus} | Cause: ${cause} | User: ${user}"
// You can get all the information available for build parameters.
def parameters = build.getAction(ParametersAction)?.parameters
parameters.each {
println "Type: ${it.class} Name: ${it.name}, Value: ${it.dump()}"
}
}
}
You will get the user ID of the user, which start the job, for sure you will not be able to get his credentials, at least not in the plain text.
Little explanation
//to get all jobs
Jenkins.instance.getAllItems(Job)
{...}
//get builds per job
def jobBuilds=it.getBuilds()
//get build cause
def cause = build.getCauses()[0] //we keep the first cause
//if triggered by an user get id, otherwise empty string
def user = cause instanceof Cause.UserIdCause? cause.getUserId():""
Related
I have a code which displays on Slack user's mail who triggered a build:
def startedBy = "${currentBuild.getBuildCauses()[0].userId}#mycompany.com"
def message = \nJob started by: ${startedBy}"
return message
which gives on Slack:
Job started by: john.doe#mycompany.com
However, when same build is triggered by another build, I get:
Job started by: null#mycompany.com
because triggering build does not include userId class in "causes":
[[_class:hudson.model.Cause$UpstreamCause,
shortDescription:Started by upstream project "Production/myjob-starter" build number 3,
upstreamBuild:3,
upstreamProject:Production/myjob-starter,
upstreamUrl:job/Productionmyjob-starter/]]
On the other side, in UI there is a phrase:
originally caused by:
Started by user John Doe
My question is - how do I always get userId, no matter if build is triggered by another build or directly by a user?
Preferably, I would like to get userId, however Started by user John Doe - shortDescription would also be valid.
==================
EDIT: The solution was to install build-user-vars plugin which globally identifies user who triggered a job. Therefore groovy script looks like this:
def startedBy = "${env.BUILD_USER_ID}#mycompany.com"
def message = \nJob started by: ${startedBy}"
return message
and it prints userId no matter of build was started directly or by another build (triggered by a user)
Is there a way to get the username when a build is aborted by a user?
Preferably using jenkins pipeline code.
When a build is aborted by a user, it logs:
Aborted by <username>
so I hope it is stored as a variable for a brief period.
Use case: username to be later used to inform the user itself or other users via email or other means of messaging.
It seems that a InterruptedBuildAction object is inserted in to the list of build action if a job is aborted. This object can be used to retrieve the user that aborted the build. I use the following function in my Jenkinsfile:
#NonCPS
def getAbortUser()
{
def causee = ''
def actions = currentBuild.getRawBuild().getActions(jenkins.model.InterruptedBuildAction)
for (action in actions) {
def causes = action.getCauses()
// on cancellation, report who cancelled the build
for (cause in causes) {
causee = cause.getUser().getDisplayName()
cause = null
}
causes = null
action = null
}
actions = null
return causee
}
In fact you can have this information with the REST API, just use the following URL with an appropriate build:
/api/json?tree=actions[causes[*]]&pretty=true
And you should be able to find the requested information under actions[causes], e.g.:
{
"_class" : "jenkins.model.InterruptedBuildAction",
"causes" : [
{
"_class" : "jenkins.model.CauseOfInterruption$UserInterruption",
"shortDescription" : "Aborted by some_user_name"
}
]
},
Unfortunately there seem to be no other solutions at the moment but to tweak Jenkin's code itself and a workaround.
Post-Build actions → Editable Email Notification → Triggers → Add Trigger → Aborted → Send To → Add → Requestor → → ... Jenkins Mailer Plugin:
Sends email to the user who initiated the build.
There's no Aborter to add.
http://<jenkins>/job/<project name>/lastBuild/api/xml shows:
...
<result>ABORTED</result>
...
but there's no info who aborted the build either.
A workaround could be to curl http://<jenkins>/job/<project name>/<build #> in a Post build task script and to grep for <p>Aborted by user username</p>.
To create a new user in Jenkins, admin needs to provide a username, emialID and password. Being an admin, is there a way to add a large number of users to Jenkins at a time by providing their username as their mail id, display name as their name and a common password*?
*Assuming that password will be reset at the time of each user logging in
I am using the below groovy script to add a user to Jenkins and provide only build permission.
import hudson.model.*
import hudson.security.*
import hudson.tasks.Mailer
def userId = args[0]
def password = args[1]
def email = args[2]
def fullName= args[3]
def instance = jenkins.model.Jenkins.instance
def existingUser = instance.securityRealm.allUsers.find {it.id == userId}
if (existingUser == null) {
def user = instance.securityRealm.createAccount(userId, password)
user.addProperty(new Mailer.UserProperty(email));
user.setFullName(fullName)
def strategy = (GlobalMatrixAuthorizationStrategy)
instance.getAuthorizationStrategy()
strategy.add(hudson.model.Item.BUILD,userId)
instance.setAuthorizationStrategy(strategy)
instance.save()
}
The script is invoked using jenkins-cli.
It is easier to connect Jenkins to LDAP. See this plugin here
looks like the Jenkins cli doesn't support add users , but check this one - using groovy script you can do it.
Creating user in Jenkins via API
if you want give specific permissions per job , maybe you can use the CLI get-job & update-job commands.
Or you can try check this one - Jenkins Add permissions to jobs using groovy it discuses almost the same ...
I have a Jenkinsfile script that tests for the possibility to perform an SVN merge and then asks the user for the permission to commit the merge.
I would like to know the username that answers the "input" step in order to write it into the commit message.
Is this possibile?
This is what hypothetically I would like to do:
outcome = input message: 'Merge trunk into branch?', ok: 'Merge'
echo "User that allowed merge: ${outcome.user}"
The input step got an optional submitterParameter, which allows to specify the key of the returned Map that should contain the user who's submitting the input dialog:
If specified, this is the name of the return value that will contain the ID of the user that approves this input.
The return value will be handled in a fashion similar to the parameters value.
Type: String
This looks then as follows:
def feedback = input(submitterParameter: 'submitter', ...)
echo "It was ${feedback.submitter} who submitted the dialog."
P.S: If anybody is interested in a full-fledged code snippet returning the user both for positive and negative feedback to the dialog (and timeout as well), I kindly point to our pipeline library.
It is not currently possible, for now only entry parameters are returned in the input step answer, as mentionned in source code :
// TODO: perhaps we should return a different object to allow the workflow to look up
// who approved it, etc?
switch (mapResult.size()) {
case 0:
return null; // no value if there's no parameter
case 1:
return mapResult.values().iterator().next();
default:
return mapResult;
}
If you'd like to restrict which user(s) can approve the input step, you can however use the submitter parameter, e.g. :
input message: 'Approve ?', submitter: 'authorized-submitter'
EDIT
Since January 2017 it is now possible to request additional parameters to be sent. Please see StephenKing answer above.
If you are not asking for any parameters on the input, then adding the submitterParameter kind of worked. It didn't add it as a parameter on the return object, instead, it turned the returned object into a string with the username in it.
def feedback = input(submitterParameter: 'submitter')
echo "It was ${feedback} who submitted the dialog."
You can do this for exceptions if you turn off the groovy-sandbox:
try {
'Deploy to production?'
node {
sh 'echo deploying'
}
} catch(e) {
def user = e.getCauses()[0].getUser()
echo "Production deployment aborted by:\n ${user}"
}
We have several JIRA issues which have over 1000 duplicated, bogus, spam-like comments. How can we quickly delete them?
Background:
We disabled a user in active directory (Exchange) but not JIRA, so JIRA kept trying to email them updates. The email server gave a bounce-back message, and JIRA dutifully logged it to the task, which caused it to send another update, and a feedback loop was born.
The messages have this format:
Delivery has failed to these recipients or groups:
mail#example.com<mail#example.com>
The e-mail address you entered couldn't be found. Please check the recipient's e-mail address and try to resend the message. If the problem continues, please contact your helpdesk.
Diagnostic information for administrators:
Generating server: emailserver.example.com
user#example.com
#550 5.1.1 RESOLVER.ADR.RecipNotFound; not found ##
Original message headers:
Received: from jiraserver.example.com (10.0.0.999) by emailserver.example.com (10.0.0.999)
with Microsoft SMTP Server id nn.n.nnn.n; Mon, 13 Jun 2016 15:57:04 -0500
Date: Mon, 13 Jun 2016 15:57:03 -0500
Our research did not discover an easy way without using purchased plug-ins such as Script Runner or "hacking" the database, which we wanted to avoid.
Note:
We came up with a solution and are posting here to share.
I created a python script to remove all comments for a specific Jira issue.
It uses the API from Jira.
'''
This script removes all comments from a specified jira issue
Please provide Jira-Issue-Key/Id, Jira-URL, Username, PAssword in the variables below.
'''
import sys
import json
import requests
import urllib3
# Jira Issue Key or Id where comments are deleted from
JIRA_ISSUE_KEY = 'TST-123'
# URL to Jira
URL_JIRA = 'https://jira.contoso.com'
# Username with enough rights to delete comments
JIRA_USERNAME = 'admin'
# Password to Jira User
JIRA_PASSWORD = 'S9ev6ZpQ4sy2VFH2_bjKKQAYRUlDfW7ujNnrIq9Lbn5w'
''' ----- ----- Do not change anything below ----- ----- '''
# Ignore SSL problem (certificate) - self signed
urllib3.disable_warnings()
# get issue comments:
# https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-issueIdOrKey-comment-get
URL_GET_COMMENT = '{0}/rest/api/latest/issue/{1}/comment'.format(URL_JIRA, JIRA_ISSUE_KEY)
# delete issue comment:
# https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-issueIdOrKey-comment-id-delete
URL_DELETE_COMMENT = '{0}/rest/api/2/issue/{1}/comment/{2}'
def user_yesno():
''' Asks user for input yes or no, responds with boolean '''
allowed_response_yes = {'yes', 'y'}
allowed_response_no = {'no', 'n'}
user_response = input().lower()
if user_response in allowed_response_yes:
return True
elif user_response in allowed_response_no:
return False
else:
sys.stdout.write("Please respond with 'yes' or 'no'")
return False
# get jira comments
RESPONSE = requests.get(URL_GET_COMMENT, verify=False, auth=(JIRA_USERNAME, JIRA_PASSWORD))
# check if http response is OK (200)
if RESPONSE.status_code != 200:
print('Exit-101: Could not connect to api [HTTP-Error: {0}]'.format(RESPONSE.status_code))
sys.exit(101)
# parse response to json
JSON_RESPONSE = json.loads(RESPONSE.text)
# get user confirmation to delete all comments for issue
print('You want to delete {0} comments for issue {1}? (yes/no)' \
.format(len(JSON_RESPONSE['comments']), JIRA_ISSUE_KEY))
if user_yesno():
for jira_comment in JSON_RESPONSE['comments']:
print('Deleting Jira comment {0}'.format(jira_comment['id']))
# send delete request
RESPONSE = requests.delete(
URL_DELETE_COMMENT.format(URL_JIRA, JIRA_ISSUE_KEY, jira_comment['id']),
verify=False, auth=(JIRA_USERNAME, JIRA_PASSWORD))
# check if http response is No Content (204)
if RESPONSE.status_code != 204:
print('Exit-102: Could not connect to api [HTTP-Error: {0}; {1}]' \
.format(RESPONSE.status_code, RESPONSE.text))
sys.exit(102)
else:
print('User abort script...')
source control: https://gist.github.com/fty4/151ee7070f2a3f9da2cfa9b1ee1c132d
Use the JIRA REST API through the Chrome JavaScript Console.
Background:
We didn't want to write a full application for what we hope is an isolated occurrence. We originally planned to use PowerShell's Invoke-WebRequest. However, authentication proved to be a challenge. The API supports Basic Authentication, though it's only recommended when using SSL, which we weren't using for our internal server. Also, our initial tests resulted in 401 errors (perhaps due to a bug).
However, the API also supports cookie-based authentication, so as long as you are generating the request from a browser which has a valid JIRA session, it just works. We chose that method.
Solution details:
First, find and review the relevant comment and issue IDs:
SELECT * FROM jira..jiraaction WHERE actiontype = 'comment' AND actionbody LIKE '%RESOLVER.ADR.RecipNotFound%';
This might be a slow query depending on the size of your JIRA data. It seems to be indexed on the issueid, so if you know that, specify it. Also, add other criteria to this query so that it only represents the comments you wish to delete.
The solution below is written for comments on a single issue, but with some additional JavaScript could be expanded to support multiple issues.
We need the list of comment IDs for use in the Chrome JavaScript console. A useful format is a comma-delimited list of strings, which you can create as follows:
SELECT '"' + CONVERT(VARCHAR(50),ID) + '", ' FROM jira..jiraaction WHERE actiontype = 'comment' AND actionbody LIKE '%RESOLVER.ADR.RecipNotFound%' AND issueid = #issueid FOR XML PATH('')
(This is not necessarily the best way to concatenate strings in SQL, but it's simple and works for this purpose.)
Now, open a new browser session and authenticate to your JIRA instance. We used Chrome, but any browser with a JavaScript console should do.
Take the string produced by that query and drop it in the JavaScript console inside of a statement like this:
CommentIDs = [StringFromSQL];
You will need to trim the trailing comma manually (or adjust the above query to do so for you). It will look like this:
CommentIDs = ["1234", "2345"];
When you run that command, you will have created a JavaScript array with all of those comment IDs.
Now we arrive at the meat of the technique. We will loop over the contents of that array and make a new AJAX call to the REST API using XMLHttpRequest (often abbreviated XHR). (There is also a jQuery option.)
for (let s of CommentIDs) {let r = new XMLHttpRequest; r.open("DELETE","http://jira.example.com/rest/api/2/issue/11111/comment/"+s,true); r.send();}
You must replace "11111" with the relevant issue ID. You can repeat this for multiple issue IDs, or you can build a multi-dimensional array and a fancier loop.
This is not elegant. It doesn't have any error handling, but you can monitor the progress using the Chrome JavaScript API.
I would use a jira-python script or a ScriptRunner groovy script. Even for a one-off bulk update, because it is easier to test and requires no database access.
Glad it worked for you though!
We solved this problem, which occurs from time to time, with ScriptRunner and a Groovy script:
// this script takes some time, when executing it in console, it takes a long time to repsonse, and then the console retunrs "null"
// - but it kepps running in the backgorund, give it some time - at least 1 second per comment and attachment to delete.
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.IssueManager
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.issue.comments.Comment
import com.atlassian.jira.issue.comments.CommentManager
import com.atlassian.jira.issue.attachment.Attachment
import com.atlassian.jira.issue.managers.DefaultAttachmentManager
import com.atlassian.jira.issue.AttachmentManager
import org.apache.log4j.Logger
import org.apache.log4j.Level
log.setLevel(Level.DEBUG)
// NRS-1959
def issueKeys = ['XS-8071', 'XS-8060', 'XS-8065', 'XRFS-26', 'NRNM-45']
def deleted_attachments = 0
def deleted_comments = 0
IssueManager issueManager = ComponentAccessor.issueManager
CommentManager commentManager = ComponentAccessor.commentManager
AttachmentManager attachmentManager = ComponentAccessor.attachmentManager
issueKeys.each{ issueKey ->
MutableIssue issue = issueManager.getIssueObject(issueKey)
List<Comment> comments = commentManager.getComments(issue)
comments.each {comment ->
if (comment.body.contains('550 5.1.1 The email account that you tried to reach does not exist')) {
log.info issueKey + " DELETE comment:"
//log.debug comment.body
commentManager.delete(comment)
deleted_comments++
} else {
log.info issueKey + " KEEP comment:"
log.debug comment.body
}
}
List<Attachment> attachments = attachmentManager.getAttachments(issue)
attachments.each {attachment ->
if (attachment.filename.equals('icon.png')) {
log.info issueKey + " DELETE attachment " + attachment.filename
attachmentManager.deleteAttachment(attachment)
deleted_attachments++
} else {
log.info issueKey + " KEEP attachment " + attachment.filename
}
}
}
log.info "${deleted_comments} deleted comments, and ${deleted_attachments} deleted attachments"
return "${deleted_comments} deleted comments, and ${deleted_attachments} deleted attachments"