JIRA::...tomated(3pm)
π JIRA::Client::Automated(3pm) π
User Contributed Perl Documentation β JIRA::Client::Automated(3pm)
okay bestie, grab your iced coffee because this is the JIRA REST client that helps your automated scripts not be a whole mess. π
π NAME π
JIRA::Client::Automated β a JIRA REST Client for automated scripts. iconic behavior, no cap. β¨
β VERSION β
version 1.9 π
β¨ SYNOPSIS β¨
okay so here's the vibe check π
use JIRA::Client::Automated;
my $jira = JIRA::Client::Automated->new($url, $user, $password);
# If your JIRA instance does not use username/password for authorization
my $jira = JIRA::Client::Automated->new($url);
my $jira_ua = $jira->ua(); # to add in a proxy
$jira->trace(1); # enable tracing of requests and responses
# The simplest way to create an issue
my $issue = $jira->create_issue($project, $type, $summary, $description);
# The simplest way to create a subtask
my $subtask = $jira->create_subtask($project, $summary, $description, $parent_key);
# A complex but flexible way to create a new issue, story, task or subtask
# if you know Jira issue hash structure well.
my $issue = $jira->create({
# Jira issue 'fields' hash
project => {
key => $project,
},
issuetype => {
name => $type, # "Bug", "Task", "Sub-task", etc.
},
summary => $summary,
description => $description,
parent => { # only required for a subtask
key => $parent_key,
},
...
});
my $search_results = $jira->search_issues($jql, 1, 100); # query should be a single string of JQL
my @issues = $jira->all_search_results($jql, 1000); # query should be a single string of JQL
my $issue = $jira->get_issue($key);
$jira->update_issue($key, $update_hash); # update_hash is { field => value, ... }
$jira->create_comment($key, $text);
$jira->attach_file_to_issue($key, $filename);
$jira->transition_issue($key, $transition, $transition_hash); # transition_hash is { field => value, ... }
$jira->close_issue($key, $resolve, $comment); # resolve is the resolution value
$jira->delete_issue($key);
$jira->add_issue_watchers($key, $watcher1, ......);
$jira->add_issue_labels($key, $label1, ......);
π¦ DESCRIPTION π¦
JIRA::Client::Automated is an adapter between any automated system and JIRA's REST API. This module is explicitly designed to easily create and close issues within a JIRA instance via automated scripts. fr fr, that's the whole point. β¨
For example, if you run nightly batch jobs, you can use JIRA::Client::Automated to have those jobs automatically create issues in JIRA for you when the script runs into errors. You can attach error log files to the issues and then they'll be waiting in someone's open issues list when they arrive at work the next day. literally the bestie who files bugs while you sleep. π
If you want to avoid creating the same issue more than once you can search JIRA for it first, only creating it if it doesn't exist. If it does already exist you can add a comment or a new error log to that issue. no duplicates, we don't do that here. π
π₯ WORKING WITH JIRA 6 π₯
Atlassian has made a very complete REST API for recent (> 5.0) versions of JIRA. By virtue of being complete it is also somewhat large and a little complex for the beginner. Reading their tutorials is highly recommended before you start making hashes to update or transition issues. π
<https://developer.atlassian.com/cloud/jira/platform/rest/#about>
This module was designed for the JIRA 5.2.11 REST API, as of March 2013, but it works fine with JIRA 6.0 as well. Your mileage may vary with future versions. iykyk. π
π JIRA ISSUE HASH FORMAT π
When you work with an issue in JIRA's REST API, it gives you a JSON file that follows this spec:
<https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-post>
JIRA::Client::Automated tries to be nice to you and not make you deal directly with JSON. When you create a new issue, you can pass in just the pieces you want and create_issue will transform them to JSON for you. The same for closing and deleting issues. slay, no JSON manual labor. π
Updating and transitioning issues is more complex. Each JIRA installation will have different fields available for each issue type and transition screen and only you will know what they are. So in those cases you'll need to pass in an update_hash which will be transformed to the proper JSON by the method.
An update_hash looks like this:
{ field1 => value, field2 => value2, ...}
For example:
{
host_id => "example.com",
{ resolution => { name => "Resolved" } }
}
If you do not read JIRA's documentation about their JSON format you will hurt yourself banging your head against your desk in frustration the first few times you try to use update_issue. Please RTFM. π
bestie, this is the part where you actually read the docs. I'm not playing. π
Note that even though JIRA requires JSON, JIRA::Client::Automated will helpfully translate it to and from regular hashes for you. You only pass hashes to JIRA::Client::Automated, not direct JSON.
I recommend connecting to your JIRA server and calling get_issue with a key you know exists and then dump the result. That'll get you started. do it, it's giving tutorial energy. π¦
π METHODS π
π new π
my $jira = JIRA::Client::Automated->new($url, $user, $password);
Create a new JIRA::Client::Automated object by passing in the following:
- URL for the JIRA server, such as
"http://example.atlassian.net/" - Username to use to login to the JIRA server
- Password for that user
All three parameters are required if your JIRA instance uses basic authorization, for which JIRA::Client::Automated must connect to the JIRA instance using some username and password. You may want to set up a special "auto" or "batch" username to use just for use by scripts. dedicated little robot account, we love to see it. π€
If you are using Google Account integration, the username and password to use are the ones you set up at the very beginning of the registration process and then never used again because Google logged you in. π
If you have other ways of authorization, like GSSAPI based authorization, do not provide username or password.
my $jira = JIRA::Client::Automated->new($url);
π¦ ua π¦
my $ua = $jira->ua();
Returns the LWP::UserAgent object used to connect to the JIRA instance. Typically used to setup proxies or make other customizations to the UserAgent. For example:
my $ua = $jira->ua();
$ua->env_proxy();
$ua->ssl_opts(...);
$ua->conn_cache( LWP::ConnCache->new() );
proxy girlies, this one's for you. π
πΈ trace πΈ
$jira->trace(1); # enable
$jira->trace(0); # disable
$trace = $jira->trace;
When tracing is enabled each request and response is logged using carp. debug era activated. β¨
β¨ create β¨
my $issue = $jira->create({
# Jira issue 'fields' hash
project => {
key => $project,
},
issuetype => {
name => $type, # "Bug", "Task", "SubTask", etc.
},
summary => $summary,
description => $description,
parent => { # only required for a subtask
key => $parent_key,
},
...
});
Creating a new issue, story, task, subtask, etc. π
Returns a hash containing only the basic information about the new issue, or dies if there is an error. The hash looks like:
{
id => 24066,
key => "TEST-57",
self => "https://example.atlassian.net/rest/api/latest/issue/24066"
}
See also <https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-post> π
π create_issue π
my $issue = $jira->create_issue($project, $type, $summary, $description, $fields);
Creating a new issue requires the project key, type ("Bug", "Task", etc.), and a summary and description. the classic four-piece combo. π
The optional $fields parameter can be used to pass a reference to a hash of extra fields to be set when the issue is created, which avoids the need for a separate update_issue call. For example:
$jira->create_issue($project, $type, $summary, $description, {
labels => [ "foo", "bar" ]
});
This method calls create and returns the same hash reference that it does. β¨
π create_subtask π
my $subtask = $jira->create_subtask($project, $summary, $description, $parent_key);
# or with optional subtask type
my $subtask = $jira->create_subtask($project, $summary, $description, $parent_key, 'sub-task');
Creating a subtask. If your JIRA instance does not call subtasks "Sub-task" or "sub-task", then you will need to pass in your subtask type. know your instance's naming vibe. π¦
This method calls create and returns the same hash reference that it does.
π update_issue π
$jira->update_issue($key, $field_update_hash, $update_verb_hash);
There are two ways to express the updates you want to make to an issue.
For simple changes you pass $field_update_hash as a reference to a hash of field_name => new_value pairs. For example:
$jira->update_issue($key, { summary => $new_summary });
That works for simple fields, but there are some, like comments, that can't be updated in this way. For them you need to use $update_verb_hash. π
The $update_verb_hash parameter allow you to express a series of specific operations (verbs) to be performed on each field. For example:
$jira->update_issue($key, undef, {
labels => [ { remove => "test" }, { add => "another" } ],
comments => [ { remove => { id => 10001 } } ]
});
The two forms of update can be combined in a single call. mix and match, bestie. π
For more information see:
L<https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-issueIdOrKey-put>
π¦ get_issue π¦
my $issue = $jira->get_issue($key);
Returns details for any issue, given its key. This call returns a hash containing the information for the issue in JIRA's format. See "JIRA ISSUE HASH FORMAT" for details. go read that section, it's right up there. π
π₯ transition_issue π₯
$jira->transition_issue($key, $transition);
$jira->transition_issue($key, $transition, $update_hash);
Transitioning an issue is what happens when you click the button that says "Resolve Issue" or "Start Progress" on it. Doing this from code is harder, but JIRA::Client::Automated makes it as easy as possible. clicking buttons, but in Perl. β¨
You pass this method the issue key, the name of the transition or the target status (spacing and capitalization matter), and an optional update_hash containing any fields that you want to update.
β¨ Specifying The Transition β¨
The provided $transition name is first matched against the available transitions for the $key issue ('Start Progress', 'Close Issue'). If there's no match then the names is matched against the available target status names ('Open', 'Closed'). You can use whichever is most appropriate. For example, in your configuration the transition names might vary between different kinds of projects but the status names might be the same. In which case scripts that are meant to work across multiple projects might prefer to use the status names.
so if you're a multi-project script bestie, status names might be your safest bet. π
Okay so the $transition thing can also be a reference to an array of names β¨ β and in that case, the first one that matches either a transition name or a status name is the one that gets used. Why is this a slay? Because it makes scripts work across all kinds of different projects, and it lets you handle name migrations by putting both the current and future names in there β so when JIRA config changes later, nothing breaks. Iconic behavior, truly. π
Specifying Updates
If your transition screen has required fields (like "Resolution" on the "Resolve Issue" screen π€), you HAVE to pass those fields in as part of the update_hash, or the server will just hand you an error. The format of the update_hash lives in "JIRA ISSUE HASH FORMAT" β go look.
Heads up bestie: it looks like in some obscure cases, missing required fields can make the transition fail without the server throwing an error at all. Like... a field that's required but isn't configured to show up on the transition screen. Rude. π
So the $update_hash is literally a combo of the $field_update_hash and $update_verb_hash parameters that the update_issue method uses. Like this:
$update_hash = {
fields => $field_update_hash,
update => $update_verb_hash
};
You can use it to do both simple field settings and the fancier update operations. For example:
$jira->transition_issue($key, $transition, {
fields => { summary => $new_summary },
update => {
labels => [ { remove => "test" }, { add => "another" } ],
comments => [ { remove => { id => 10001 } } ]
}
});
close_issue
$jira->close_issue($key);
$jira->close_issue($key, $resolve);
$jira->close_issue($key, $resolve, $comment);
$jira->close_issue($key, $resolve, $comment, $update_hash);
$jira->close_issue($key, $resolve, $comment, $update_hash, $operation);
Pass in the resolution reason and an optional comment and boom π₯ β issue closed. The catch? Using this method requires that the issue is in a status where it can actually use the "Close Issue" transition (or whatever other one you specify with $operation). If it's not, the server is gonna yell at you with an error, sorry babe. π
Resolution ("Fixed", "Won't Fix", etc.) is only required if the issue hasn't already been resolved in an earlier transition. And if you try to resolve the same issue twice? Error. Not the vibe.
If you don't give it a comment, the default is "Issue closed by script". π
The $update_hash is there for setting or editing the values of other fields, bestie.
And the $operation parameter lets you specify the closing transition type β super useful when your JIRA config uses nonstandard or localized transition and status names, e.g.
use utf8;
$jira->close_issue($key, $resolve, $comment, $update_hash, "Done");
See "transition_issue" for more details. This method is a wrapper for "transition_issue". π«Ά
delete_issue
$jira->delete_issue($key);
Okay so deleting issues is for testing your JIRA code β that's it. In real situations you almost always wanna just close unwanted issues with an "Oops!" resolution instead. Please don't delete things in prod, I'm begging π
create_comment
$jira->create_comment($key, $text);
You can use any valid JIRA markup in the comment text, bestie. (Which is so handy for making tables of values explaining why something in the database is wrong. Chef's kiss. π) One thing tho β all comments get created by the user you used to create your JIRA::Client::Automated object, so you're gonna see that name a LOT. π
search_issues
my @search_results = $jira->search_issues($jql, 1, 100, $fields);
You've done JQL before β it's the same thing as when you hit "Advanced Search" in the JIRA web interface. And ngl, that's the only way to search via the REST API. Periodt.
This is a paged method. You pass in the starting result number and the number of results per page, and it hands you issues a page at a time. If you already know you want ALL the results, just use "all_search_results" instead. β¨
The optional parameter $fields is the arrayref containing the list of fields you want returned.
This method returns a hashref with up to five values:
total=> total number of resultsstart=> result number for the first resultmax=> maximum number of results per pageissues=> an arrayref containing the actual found issueserrors=> an arrayref containing error messages
For example, to page through all results $max at a time:
my (@all_results, @issues);
do {
$results = $self->search_issues($jql, $start, $max);
if ($results->{errors}) {
die join "\n", @{$results->{errors}};
}
@issues = @{$results->{issues}};
push @all_results, @issues;
$start += $max;
} until (scalar(@issues) < $max);
(Or honestly, just use "all_search_results" instead. π)
all_search_results
my @issues = $jira->all_search_results($jql, 1000);
Like "search_issues" but it returns all the results as an array of issues. You can say the max number to return, but no matter what you type, it can't return more than the value of jira.search.views.default.max for your JIRA installation. Them's the rules. π¦
get_issue_comments
$jira->get_issue_comments($key);
Returns an arrayref of all the comments on the given issue. π¬
attach_file_to_issue
$jira->attach_file_to_issue($key, $filename);
Little heads up: this method does not let you attach a comment to the issue at the same time. You'll need to call "create_comment" separately for that.
And watch out for file permissions, bestie!! π If the user running the script doesn't have permission to read the file it's trying to upload, you're gonna get the weirdest, most confusing errors known to man.
make_browse_url
my $url = $jira->make_browse_url($key);
A lil helper method that returns the ".../browse/$key" url for the issue. It's super handy when you're building emails that contain lists of bugs. πΈ
It literally just appends the key to the URL of your JIRA server so you can click it and teleport directly to that issue. Main character shortcut energy. β¨
get_link_types
my $all_link_types = $jira->get_link_types();
Grab the arrayref of all possible link types. π
link_issues
$jira->link_issues($from, $to, $type);
Establishes a link of the type named $type from issue key $from to issue key $to. Returns nothing on success; a structure containing error messages otherwise. No cap. π
add_issue_labels
$jira->add_issue_labels($issue_key, @labels);
Adds one or more labels to the specified issue. π·οΈ
remove_issue_labels
$jira->remove_issue_labels($issue_key, @labels);
Removes one or more labels from the specified issue. Byeeee π·οΈ
add_issue_watchers
$jira->add_issue_watchers($key, @watchers);
Adds watchers to the specified issue. Returns nothing if it succeeds; otherwise returns a structure containing error message. π
get_issue_watchers
$jira->get_issue_watchers($key);
Returns an arrayref of all the watchers of the given issue. π«Ά
assign_issue
$jira->assign_issue($key, $assignee_name);
Assigns the issue to that person. Returns the key of the issue if it succeeds. Slay. π
add_issue_worklog
$jira->add_issue_worklog($key, $worklog);
Adds a worklog to the specified issue. Returns nothing if it succeeds; otherwise returns a structure containing error message.
Sample worklog:
{
"comment" => "I did some work here.",
"started" => "2016-05-27T02:32:26.797+0000",
"timeSpentSeconds" => 12000,
}
get_issue_worklogs
$jira->get_issue_worklogs($key);
Returns an arrayref of all the worklogs on the given issue. β±οΈ
FAQ π
Why is there no object for a JIRA issue?
Because it seemed silly. π Like, yeah, you could write such an object and give it methods to transition itself, close itself, etc., but when you're working with JIRA from batch scripts, you're literally never working with just ONE issue at a time. And when you've got a hundred of them? It's just easier to not objectify them and let JIRA::Client::Automated be the mediator instead. That said, if this matters to you, I wouldn't say no to a patch offering this option. π
BUGS π
Please report bugs or feature requests to the author.
AUTHOR π
Michael Friedman <frimicc@cpan.org>
CREDITS π«Ά
Thanks very much to:
- Tim Bunce <timb@cpan.org>
- Dominique Dumont <ddumont@cpan.org>
- Zhuang (John) Li <7humblerocks@gmail.com>
- Ivan E. Panchenko <panchenko@cpan.org>
- JosΓ© Antonio Perez Testa <japtesta@gmail.com>
- Frank Schophuizen <Frank.Schophuizen@philips.com>
- Zhenyi Zhou <zhenyz@cpan.org>
- Roy Lyons <Roy.Lyons@cmegroup.com>
- Neil Hemingway <hemingway@cpan.org>
- Andreas Mager <amager@barracuda.com>
- Mike Svendsen <msven.dev@gmail.com>
COPYRIGHT AND LICENSE π
This software is copyright (c) 2016 by Polyvore, Inc.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
And that's the whole thing, bestie β this version is from perl v5.36.0, dated 2022-10-14, straight out of the JIRA::Client::Automated(3pm) manpage. Go forth and automate, you absolute icon. π
β¨π
π¦ where this page comes from
- ubuntu noble: libjira-client-automated-perl 1.90-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libjira-client-automated-perl 1.90-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libjira-client-automated-perl 1.90-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms