Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add create issue command #24

Merged
merged 1 commit into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Please follow along with the [initial release discussion](https://github.com/kid

## ⚡️ Requirements

- Install [http.nvim](https://github.com/jcdickinson/http.nvim)
- Instead of using the Jira API directly in all cases, this plugin leverages the [jira-cli tool](https://github.com/ankitpokhrel/jira-cli) to interact with Jira. You'll need to install it and configure it with your Jira credentials in order to use some of the features of this plugin.

## 📦 Installation

Expand Down Expand Up @@ -82,6 +82,7 @@ There is only an Jira <object> <action> [arguments] command.
|---|---|---|
| issue | view [issue_id] | View the given issue, if none provided it will attempt to extract one out of the current git branch (disabled via `use_git_branch_issue_id`), else falls back to a prompt |
| | transition [issue_id] [transition_name] | Transition the ticket to a given status. Will attempt to extract issue ID from git branch, and will prompt if no options given
| | create | Create a new issue. Will prompt for all required fields. It will also prompt you to create a branch with the created issue ID in the name.

Additionally, the transition command has a Telescope picker.

Expand All @@ -95,4 +96,5 @@ There are no default mappings, but you can create your own. Here's an example:
local t = require 'telescope'
vim.keymap.set('n', '<leader>jv', '<cmd>Jira issue view<cr>', {})
vim.keymap.set('n', '<leader>jt', t.extensions.jira.transitions, {})
vim.keymap.set('n', '<leader>jc', '<cmd>Jira issue create<cr>', {})
```
42 changes: 42 additions & 0 deletions lua/jira/api_client.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
local utils = require 'jira.utils'
local config = require 'jira.config'
local curl = require 'plenary.curl'
local _, Job = pcall(require, 'plenary.job')

local M = {}
local transition_cache = {}
Expand Down Expand Up @@ -81,4 +82,45 @@ M.transition_issue = function(transition_id, issue_id)
})
end

-- Creates an issue with the given type, summary, and optional description file path
-- @param issue table - the issue to create
-- @param issue.type string - the type of the issue
-- @param issue.summary string - the summary of the issue
-- @param issue.descriptionFile string - the path to the file containing the description
-- @return table - the issue id and link
-- e.g. { issue_id = 'ABC-1234', link = 'https://blah.atlassian.net/ABC-1234' }
M.create_issue = function(issue)
local type = issue.type
local summary = issue.summary
local descriptionFilePath = issue.descriptionFile
assert(type, 'Missing issue type')
assert(summary, 'Missing issue summary')

local args = { 'issue', 'create', '-t', type, '-s', summary }
if descriptionFilePath then
table.insert(args, '-T')
table.insert(args, descriptionFilePath)
end

local job = Job:new {
enable_recording = true,
interactive = false,
command = 'jira',
args = args,
on_exit = function(_, code)
if code ~= 0 then
vim.nofity('Error creating issue', vim.log.levels.ERROR)
end
end,
}
job:sync()
local lines = job:result()
-- last line is the issue id
-- e.g. 'Issue created: ABC-1234'
-- extract the issue id:
-- 'ABC-1234'
local issue_id = lines[#lines]:match '([A-Z]+%-[0-9]+)'
return { issue_id = issue_id, link = lines[#lines] }
end

return M
108 changes: 108 additions & 0 deletions lua/jira/commands.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
local api_client = require 'jira.api_client'
local utils = require 'jira.utils'
local M = {}
local config = require 'jira.config'

M.setup = function()
vim.api.nvim_create_user_command('Jira', function(opts)
Expand All @@ -16,6 +17,9 @@ M.setup = function()
transition = function(transition_name, issue_id)
M.transition_issue_name(transition_name, issue_id)
end,
create = function(args)
M.create_issue(args)
end,
},
}
end
Expand Down Expand Up @@ -106,4 +110,108 @@ function M.transition_issue_name(transition_name, issue_id)
end
end

local function get_char_input()
local char = vim.fn.getchar()
-- if char is escape
if char == 27 then
return nil
end
return vim.fn.nr2char(char)
end

local function clear_prompt()
vim.api.nvim_command 'normal! :'
end

-- create a git branch with the issue id
-- @param issue_id string - the issue id to use for the branch
-- @param branch_suffix string - the suffix to append to the branch name
local create_git_branch = function(issue_id, branch_suffix)
local branch_from = config.get_config().git_trunk_branch
vim.ui.input({
prompt = 'Enter branch to create branch from [esc to cancel]: ',
default = branch_from or 'main',
}, function(value)
branch_from = value
end)

if not branch_from or branch_from == '' then
return
end

utils.create_git_branch(issue_id, branch_suffix, branch_from)
end

function M.create_issue(issueType)
if not issueType then
vim.ui.input({
prompt = 'Issue type: ',
default = 'Task',
}, function(value)
issueType = value
end)
end
if not issueType or issueType == '' then
return
end

local summary
vim.ui.input({
prompt = 'Summary: ',
}, function(value)
summary = value
end)

if not summary or summary == '' then
return
end

clear_prompt()
print 'Edit description? (y/N)'
local res = get_char_input()
clear_prompt()
-- if user cancels
if res == nil then
return
end
local edit_description = false
if res:match '\r' or res:match '\n' or res:match 'n' or res:match 'N' then
edit_description = false
end
if res:match 'y' or res:match 'Y' then
edit_description = true
end

if not edit_description then
local r = api_client.create_issue {
type = issueType,
summary = summary,
}
vim.notify(r.link)
create_git_branch(r.issue_id, summary)
return
end

local tempfile = vim.fn.tempname()
local bufnr = vim.api.nvim_create_buf(true, false)
vim.api.nvim_buf_set_name(bufnr, tempfile)
vim.api.nvim_buf_set_option(bufnr, 'filetype', 'markdown')
vim.cmd 'split'
local win = vim.api.nvim_get_current_win()
vim.api.nvim_win_set_buf(win, bufnr)
vim.api.nvim_create_autocmd('BufWritePost', {
buffer = bufnr,
callback = function()
local r = api_client.create_issue {
type = issueType,
summary = summary,
descriptionFile = tempfile,
}
vim.notify(r.link)
create_git_branch(r.issue_id, summary)
vim.api.nvim_buf_delete(bufnr, { force = true })
end,
})
end

return M
5 changes: 5 additions & 0 deletions lua/jira/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ M.defaults = {
token = vim.env.JIRA_API_TOKEN,
},
use_git_branch_issue_id = true,
git_branch_prefix = 'feature/',
}

local config = {}
Expand All @@ -15,6 +16,10 @@ function M.get_config()
return config or M.defaults
end

function M.set_current_issue(issue_id)
config.current_issue = issue_id
end

function M.setup(user_config)
user_config = user_config or {}
config = vim.tbl_deep_extend('force', M.defaults, user_config)
Expand Down
21 changes: 21 additions & 0 deletions lua/jira/utils.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
local M = {}
local _, Job = pcall(require, 'plenary.job')
local config = require 'jira.config'

function Set(list)
local set = {}
Expand Down Expand Up @@ -324,4 +326,23 @@ M.get_issue_id = function(issue_id)
return issue_id
end

M.create_git_branch = function(issue_id, branch_suffix, branch_from)
-- replace whitespace with hyphens
branch_suffix = branch_suffix:gsub('%s', '-')
-- lowercase and strip all non-alphanumeric characters
branch_suffix = branch_suffix:gsub('[^%w-]+', ''):lower()

local branch_prefix = config.get_config().git_branch_prefix
local branch = branch_prefix .. issue_id .. '/' .. branch_suffix
Job:new({
command = 'git',
args = { 'branch', branch, branch_from or 'main' },
on_exit = function(_, code)
if code ~= 0 then
vim.notify('Error creating branch', vim.log.levels.ERROR)
end
end,
}):sync()
end

return M
Loading