Created
November 9, 2022 16:37
-
-
Save phallstrom/40c4667d8d1d314a2132cffb2e03f080 to your computer and use it in GitHub Desktop.
sailfish
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### sailfish | |
Sailfish is a ruby script that is meant to turn a git commit message into a | |
JIRA ticket and then update that commit message to reference the newly created | |
JIRA ticket. | |
To use it you will need Ruby installed and a few gems (see top of script file | |
itself for details). | |
You will also need to create `~/.sailfish` with contents similar to the following: | |
``` | |
--- | |
username: youremail@example.com | |
password: xxxxxxxxxxxxxxxxxxxxxxxx | |
project_key: ABC | |
board_name: App Dev Team | |
sprint_prefix: Dev Sprint | |
``` | |
You can also set defaults for issue type, points, priority, and epic. See the | |
script file for details. | |
Once configured, to use it write a normal git commit message to a file and then run: | |
% path/to/devfu/sailfish path/to/commit/msg | |
Ideally you'd configure your git tooling to do this for you. If you use the Git | |
CLI and VIM, checkout [vim-sailfish](https://github.com/phallstrom/vim-sailfish). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# NOTE: You will almost certainly have to tweak some of the parameters sent to JIRA | |
# depending on how your JIRA has been setup and what fields are required, etc. Pay close | |
# attention to any "custom_field_12345" sections of the code. | |
# https://developer.atlassian.com/cloud/jira/platform/rest/v2/ | |
# https://developer.atlassian.com/cloud/jira/platform/rest/v3/ | |
# https://docs.atlassian.com/jira-software/REST/7.3.1/ | |
require "rubygems" | |
require "yaml" | |
require "pry" | |
require "http" | |
################################################################################ | |
config = { | |
host: "example.atlassian.net", | |
issue_type: "Story", # Story, Bug, Task | |
points: 2, | |
priority: 3, | |
epic: nil, # ex: RKT-602 | |
} | |
################################################################################ | |
[ ENV["HOME"], Dir.pwd ].each do |dir| | |
config_file = File.join(dir, ".sailfish") | |
next unless File.exist?(config_file) | |
config.merge!(YAML.load(File.read(config_file), symbolize_names: true)) | |
end | |
raise "You must set 'username' in the configuration" unless config[:username] | |
raise "You must set 'password' in the configuration" unless config[:password] | |
raise "You must set 'project_key' in the configuration" unless config[:project_key] | |
raise "You must set 'board_name' in the configuration" unless config[:board_name] | |
raise "You must set 'sprint_prefix' in the configuration" unless config[:sprint_prefix] | |
################################################################################ | |
project_key = config[:project_key] | |
board_name = config[:board_name] | |
origin = config[:origin] | |
issue_type = config[:issue_type] | |
points = config[:points] | |
priority = config[:priority] | |
epic = config[:epic] | |
################################################################################ | |
commit_file = ARGV[0] | |
raise "Commit file '#{commit_file}' does not exist" unless File.exist?(commit_file) | |
commit_msg = File.readlines(commit_file) | |
summary = commit_msg.first.strip | |
description = commit_msg.drop(1).reject { |l| l.start_with?("#") }.join.strip | |
raise "Commit file does not have a summary" if summary.to_s.empty? | |
raise "Commit file does not have a description" if description.to_s.empty? | |
raise "Commit file already references a ticket" if description.match?(/^(fixes|refs|closes) #{project_key}-\d/) | |
################################################################################ | |
http = HTTP.basic_auth(user: config[:username], pass: config[:password]) | |
################################################################################ | |
res = http.get("https://#{config[:host]}/rest/agile/1.0/board") | |
board = JSON.parse(res.body.to_s)["values"].detect { |e| e["location"]["projectKey"] == project_key && e["name"] == board_name} | |
board_id = board["id"] | |
project_id = board["location"]["projectId"] | |
raise("Unable to find board id for project key '#{project_key}'") if board_id.nil? | |
res = http.get("https://#{config[:host]}/rest/api/3/issuetype/project?projectId=#{project_id}") | |
issue_type_id = JSON.parse(res.body.to_s).detect { |e| e["name"] == issue_type }["id"] | |
raise("Unable to find issue type id") if issue_type_id.nil? | |
res = http.get("https://#{config[:host]}/rest/api/3/myself") | |
assignee = JSON.parse(res.body.to_s)["accountId"] | |
raise("Unable to find assignee") if assignee.nil? | |
res = http.get("https://#{config[:host]}/rest/agile/1.0/board/#{board_id}/sprint?state=active") | |
sprint_id = JSON.parse(res.body.to_s)["values"].select { |e| e["name"].match?(/^#{config[:sprint_prefix]}/i) }.sort_by { |e| e["endDate"] }.last["id"] | |
raise("Unable to find active sprint") if sprint_id.nil? | |
res = http.get("https://#{config[:host]}/rest/api/3/field") | |
fields = JSON.parse(res.body.to_s) | |
story_points_field = fields.detect { |e| e["name"] == "Story Points" }["key"].to_sym | |
sprint_field = fields.detect { |e| e["name"] == "Sprint" }["key"].to_sym | |
epic_field = fields.detect { |e| e["name"] == "Epic Link" }["key"].to_sym | |
devteam_field = fields.detect { |e| e["name"] == "YourDev Team" && e["scope"].nil? }["key"].to_sym | |
################################################################################ | |
begin | |
res = http.post("https://#{config[:host]}/rest/api/2/issue", json: { | |
fields: { | |
summary: summary, | |
description: description, | |
project: { id: project_id.to_s }, | |
issuetype: { id: issue_type_id }, | |
priority: { id: priority.to_s }, | |
assignee: { id: assignee }, | |
sprint_field => sprint_id.to_i, | |
epic_field => epic, | |
devteam_field => [{ id: "10116"}] # "Your App Dev Team" | |
} | |
}) | |
if res.code == 201 | |
ticket = JSON.parse(res.body.to_s) | |
File.write(commit_file, commit_msg.insert(1, "\n", "fixes [#{ticket["key"]}]", "\n").join) | |
begin | |
res = http.get("https://#{config[:host]}/rest/api/2/issue/#{ticket["key"]}/transitions") | |
transition_id = JSON.parse(res.body.to_s)["transitions"].detect { |e| e["name"] == "In Progress" }["id"] | |
http.post("https://#{config[:host]}/rest/api/2/issue/#{ticket["key"]}/transitions", json: { | |
transition: { id: transition_id.to_i } | |
}) | |
# fields: { story_points_field => points.to_i } | |
rescue | |
end | |
else | |
puts | |
puts "!!! ERROR !!!" | |
puts | |
begin | |
puts JSON.pretty_generate(JSON.parse(res.body.to_s)) | |
rescue | |
puts res.body.to_s | |
end | |
puts | |
end | |
rescue StandardError => e | |
puts | |
puts "!!! ERROR !!!" | |
puts | |
puts e.to_s | |
puts | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment