jira-cli 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +15 -1
- data/lib/jira.rb +9 -0
- data/lib/jira/api.rb +51 -0
- data/lib/jira/constants.rb +1 -1
- data/lib/jira/core.rb +125 -0
- data/lib/jira/format.rb +35 -0
- data/lib/jira/install.rb +4 -3
- data/lib/jira/lookup.rb +47 -70
- data/lib/jira/mixins.rb +0 -71
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24aaafc8c0cbd9ae1830d16781ccfdae4d57c695
|
4
|
+
data.tar.gz: 64d3470c5ae4c3b0bcb5f9adfa403e0d0c4f0cf1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff546c7868f58330d07439fb52cbf01f9db9a076696f3b7d77b1ebde128ac5ea96f246484eb34e9194e262b75f748aba7ac3b7de6487507386e23cd60218cc8c
|
7
|
+
data.tar.gz: 88acd2274a38804e271b0a0f9dbbb00d19ea9e26cf3577cb72fba1f34d0cb3c8539e5030b31896b450a178379459a0fd6cdeb4671c140bfcb0dfaadab2eb0f0e
|
data/README.md
CHANGED
@@ -1,4 +1,18 @@
|
|
1
|
-
|
1
|
+
JIRA CLI
|
2
2
|
========
|
3
3
|
|
4
4
|
Ruby gem CLI tool used to manage JIRA workflows leveraging git
|
5
|
+
|
6
|
+
Disclaimer
|
7
|
+
----------
|
8
|
+
This tool is in very early alpha and its architecture and commands
|
9
|
+
are expected to change drastically. Please only use this tool for testing
|
10
|
+
purposes.
|
11
|
+
|
12
|
+
Installation
|
13
|
+
------------
|
14
|
+
cd path/to/jira/repo
|
15
|
+
jira install
|
16
|
+
|
17
|
+
Note: Authentication files are expected to drastically change. Currently, they
|
18
|
+
are completely unencrypted. Use are your own risk... (see disclaimer above)
|
data/lib/jira.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'fileutils'
|
3
3
|
require 'jira/constants'
|
4
|
+
require 'jira/core'
|
5
|
+
require 'jira/api'
|
6
|
+
require 'jira/format'
|
4
7
|
require 'jira/mixins'
|
5
8
|
require 'jira/version'
|
6
9
|
require 'jira/install'
|
@@ -9,5 +12,11 @@ require 'jira/lookup'
|
|
9
12
|
module Jira
|
10
13
|
class CLI < Thor
|
11
14
|
|
15
|
+
def initialize(args=[], options={}, config={})
|
16
|
+
super
|
17
|
+
Jira::Core.setup
|
18
|
+
@api = Jira::API.new
|
19
|
+
end
|
20
|
+
|
12
21
|
end
|
13
22
|
end
|
data/lib/jira/api.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
module Jira
|
2
|
+
class API
|
3
|
+
|
4
|
+
#
|
5
|
+
# Initialize Jira::API
|
6
|
+
#
|
7
|
+
def initialize
|
8
|
+
@client = Faraday.new
|
9
|
+
@client.basic_auth(Jira::Core.username, Jira::Core.password)
|
10
|
+
end
|
11
|
+
|
12
|
+
#
|
13
|
+
# Issue an API GET request and return parsed JSON
|
14
|
+
#
|
15
|
+
# @param path [String] API path
|
16
|
+
#
|
17
|
+
# @return [JSON] parsed API response
|
18
|
+
#
|
19
|
+
def get(path)
|
20
|
+
response = @client.get(self.endpoint(path))
|
21
|
+
JSON.parse(response.body)
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Issue an API POST request and return parsed JSON
|
26
|
+
#
|
27
|
+
# @param path [String] API path
|
28
|
+
# @param params [Hash] params to post
|
29
|
+
#
|
30
|
+
# @return [JSON] parsed API response
|
31
|
+
#
|
32
|
+
def post(path, params)
|
33
|
+
response = @client.post(self.endpoint(path), params)
|
34
|
+
JSON.parse(response.body)
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
#
|
40
|
+
# Returns the full JIRA REST API endpoint
|
41
|
+
#
|
42
|
+
# @param path [String] API path
|
43
|
+
#
|
44
|
+
# @return [String] API endpoint
|
45
|
+
#
|
46
|
+
def endpoint(path)
|
47
|
+
"#{Jira::Core.url}/rest/api/2/#{path}"
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
data/lib/jira/constants.rb
CHANGED
data/lib/jira/core.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
module Jira
|
2
|
+
class Core
|
3
|
+
class << self
|
4
|
+
|
5
|
+
#
|
6
|
+
# Memoizes url, username, and password
|
7
|
+
#
|
8
|
+
def setup
|
9
|
+
self.url
|
10
|
+
self.auth
|
11
|
+
end
|
12
|
+
|
13
|
+
### Virtual Attributes
|
14
|
+
|
15
|
+
#
|
16
|
+
# @return [String] JIRA project endpoint
|
17
|
+
#
|
18
|
+
def url
|
19
|
+
@url ||= self.read(self.url_path)
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# @return [String] JIRA username
|
24
|
+
#
|
25
|
+
def username
|
26
|
+
@username ||= self.auth.first
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# @return [String] JIRA password
|
31
|
+
#
|
32
|
+
def password
|
33
|
+
@password ||= self.auth.last
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Determines whether or not the input ticket matches the expected JIRA
|
38
|
+
# ticketing syntax.
|
39
|
+
#
|
40
|
+
# @param ticket [String] input ticket name
|
41
|
+
#
|
42
|
+
# @return [Boolean] whether input string matches JIRA ticket syntax
|
43
|
+
#
|
44
|
+
def ticket?(ticket)
|
45
|
+
!!ticket[/^[a-zA-Z]+-[0-9]+$/]
|
46
|
+
end
|
47
|
+
|
48
|
+
### Relevant Paths
|
49
|
+
|
50
|
+
#
|
51
|
+
# @return [String] path to .jira-url file
|
52
|
+
#
|
53
|
+
def url_path
|
54
|
+
@url_path ||= self.root_path + "/.jira-url"
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# @return [String] path to .jira-auth file
|
59
|
+
#
|
60
|
+
def auth_path
|
61
|
+
@auth_path ||= self.root_path + "/.jira-auth"
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# @return [String] path of root git directory
|
66
|
+
#
|
67
|
+
def root_path
|
68
|
+
return @root_path if !@root_path.nil?
|
69
|
+
if !system('git rev-parse')
|
70
|
+
puts "JIRA commands can only be run within a git repository."
|
71
|
+
abort
|
72
|
+
end
|
73
|
+
@root_path ||= `git rev-parse --show-toplevel`.strip
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
|
78
|
+
#
|
79
|
+
# Determines and parses the auth file
|
80
|
+
#
|
81
|
+
# @return [String] JIRA username
|
82
|
+
# @return [String] JIRA password
|
83
|
+
#
|
84
|
+
def auth
|
85
|
+
self.read(self.auth_path).split(':')
|
86
|
+
end
|
87
|
+
|
88
|
+
### Core Actions
|
89
|
+
|
90
|
+
#
|
91
|
+
# Discards memozied class variables
|
92
|
+
#
|
93
|
+
def discard_memoized
|
94
|
+
@url = nil
|
95
|
+
@username = nil
|
96
|
+
@password = nil
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Validates the location and reads the contents of the input path
|
101
|
+
#
|
102
|
+
# @param path [String] path of file to read
|
103
|
+
#
|
104
|
+
# @return [String] contents of the file at the input path
|
105
|
+
#
|
106
|
+
def read(path)
|
107
|
+
self.validate_path!(path)
|
108
|
+
File.read(path).strip
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Aborts command if no file at the input path exists.
|
113
|
+
#
|
114
|
+
# @param path [String] path to validate
|
115
|
+
#
|
116
|
+
def validate_path!(path)
|
117
|
+
if !File.exists?(path)
|
118
|
+
say "Please run `jira install` before running this command."
|
119
|
+
abort
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/jira/format.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Jira
|
2
|
+
class Format
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def star
|
6
|
+
"#{Thor::Shell::Color::BOLD}"\
|
7
|
+
"#{Thor::Shell::Color::YELLOW}"\
|
8
|
+
"*"\
|
9
|
+
"#{Thor::Shell::Color::CLEAR}"
|
10
|
+
end
|
11
|
+
|
12
|
+
def ticket(ticket)
|
13
|
+
"#{Thor::Shell::Color::RED}"\
|
14
|
+
"#{ticket}"\
|
15
|
+
"#{Thor::Shell::Color::CLEAR}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def status(status)
|
19
|
+
"["\
|
20
|
+
"#{Thor::Shell::Color::BLUE}"\
|
21
|
+
"#{status}"\
|
22
|
+
"#{Thor::Shell::Color::CLEAR}"\
|
23
|
+
"]".center(26)
|
24
|
+
end
|
25
|
+
|
26
|
+
def summary(summary)
|
27
|
+
"#{Thor::Shell::Color::BOLD}"\
|
28
|
+
"#{Thor::Shell::Color::WHITE}"\
|
29
|
+
"#{summary}"\
|
30
|
+
"#{Thor::Shell::Color::CLEAR}"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/jira/install.rb
CHANGED
@@ -7,11 +7,12 @@ module Jira
|
|
7
7
|
|
8
8
|
desc "install", "Guides the user through JIRA installation"
|
9
9
|
def install
|
10
|
-
|
10
|
+
|
11
|
+
create_file Jira::Core.url_path, nil, verbose:false do
|
11
12
|
self.cli.ask("Enter your JIRA URL: ")
|
12
13
|
end
|
13
14
|
|
14
|
-
create_file
|
15
|
+
create_file Jira::Core.auth_path, nil, verbose:false do
|
15
16
|
username = self.cli.ask("Enter your JIRA username: ")
|
16
17
|
password = self.cli.ask("Enter your JIRA password: ") do |q|
|
17
18
|
q.echo = false
|
@@ -19,7 +20,7 @@ module Jira
|
|
19
20
|
"#{username}:#{password}"
|
20
21
|
end
|
21
22
|
|
22
|
-
|
23
|
+
Jira::Core.send(:discard_memoized)
|
23
24
|
end
|
24
25
|
|
25
26
|
end
|
data/lib/jira/lookup.rb
CHANGED
@@ -1,42 +1,52 @@
|
|
1
1
|
module Jira
|
2
2
|
class CLI < Thor
|
3
3
|
|
4
|
-
desc "
|
5
|
-
def
|
6
|
-
ticket ||=
|
7
|
-
|
8
|
-
summary = json['fields']['summary']
|
9
|
-
status = json['fields']['status']['name']
|
10
|
-
self.mutex.synchronize do
|
11
|
-
say "#{self.colored_ticket(ticket)} "\
|
12
|
-
"#{self.colored_status(status).center(26)} "\
|
13
|
-
"#{self.colored_summary(summary)}"
|
14
|
-
end
|
4
|
+
desc "describe", "Describes the input ticket"
|
5
|
+
def describe(ticket=nil)
|
6
|
+
ticket ||= `git rev-parse --abbrev-ref HEAD`.strip
|
7
|
+
output_summary(ticket)
|
15
8
|
end
|
16
9
|
|
17
|
-
desc "all", "
|
10
|
+
desc "all", "Describes all local branches that match JIRA ticketing syntax"
|
18
11
|
def all
|
19
|
-
|
20
|
-
|
12
|
+
# determine which local branches match JIRA ticket syntax
|
13
|
+
# TODO - move to Jira::Git
|
14
|
+
tickets = {
|
15
|
+
current: nil,
|
16
|
+
others: []
|
17
|
+
}
|
18
|
+
branches = `git branch`.strip.split("\n")
|
21
19
|
branches.each do |branch|
|
22
|
-
|
23
|
-
if
|
24
|
-
|
20
|
+
ticket = branch.delete('*').strip
|
21
|
+
if Jira::Core.ticket?(ticket)
|
22
|
+
if branch.include?('*')
|
23
|
+
tickets[:current] = ticket
|
24
|
+
else
|
25
|
+
tickets[:others] << ticket
|
26
|
+
end
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
30
|
+
|
31
|
+
# asynchronously fetch and describe tickets
|
32
|
+
output = ""
|
28
33
|
threads = []
|
29
|
-
|
30
|
-
|
34
|
+
threads << Thread.new{ puts description(tickets[:current], true) }
|
35
|
+
mutex = Mutex.new
|
36
|
+
tickets[:others].each do |ticket|
|
37
|
+
threads << Thread.new do
|
38
|
+
out = description(ticket) + "\n"
|
39
|
+
mutex.synchronize{ output << out }
|
40
|
+
end
|
31
41
|
end
|
32
42
|
threads.each{ |thread| thread.join }
|
43
|
+
|
44
|
+
puts output
|
33
45
|
end
|
34
46
|
|
35
47
|
desc "comment", "Add a comment to the input ticket"
|
36
48
|
def comment(ticket=nil)
|
37
49
|
say "Coming soon"
|
38
|
-
#ticket ||= ex('get rev-parse --abbrev-ref HEAD')
|
39
|
-
#json = self.api_post("issue/#{ticket}")
|
40
50
|
end
|
41
51
|
|
42
52
|
desc "transition", "Transitions the input ticket to the next state"
|
@@ -46,55 +56,22 @@ module Jira
|
|
46
56
|
|
47
57
|
protected
|
48
58
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
def
|
58
|
-
"
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
def colored_summary(summary)
|
66
|
-
"#{Thor::Shell::Color::BOLD}"\
|
67
|
-
"#{Thor::Shell::Color::WHITE}"\
|
68
|
-
"#{summary}"\
|
69
|
-
"#{Thor::Shell::Color::CLEAR}"
|
70
|
-
end
|
71
|
-
|
72
|
-
def api_get(path)
|
73
|
-
response = self.client.get self.api_path(path)
|
74
|
-
return JSON.parse(response.body)
|
75
|
-
end
|
76
|
-
|
77
|
-
def api_post(path, params)
|
78
|
-
response = self.client.post self.api_path(path), params
|
79
|
-
return JSON.parse(response)
|
80
|
-
end
|
81
|
-
|
82
|
-
def api_path(path)
|
83
|
-
"#{self.jira_url}/rest/api/2/#{path}"
|
84
|
-
end
|
85
|
-
|
86
|
-
def mutex
|
87
|
-
@mutex ||= Mutex.new
|
88
|
-
end
|
89
|
-
|
90
|
-
def client
|
91
|
-
self.mutex.synchronize do
|
92
|
-
return @client if !@client.nil?
|
93
|
-
@client = Faraday.new
|
94
|
-
username, password = self.jira_auth
|
95
|
-
@client.basic_auth(username, password)
|
96
|
-
return @client
|
97
|
-
end
|
59
|
+
#
|
60
|
+
# Returns a formatted description of the input ticket
|
61
|
+
#
|
62
|
+
# @param ticket [String] the ticket to describe
|
63
|
+
# @param star [Boolean] if true, adds a * indicator
|
64
|
+
#
|
65
|
+
# @return [String] formatted summary string
|
66
|
+
#
|
67
|
+
def description(ticket, star=false)
|
68
|
+
json = @api.get("issue/#{ticket}")
|
69
|
+
summary = json['fields']['summary']
|
70
|
+
status = json['fields']['status']['name']
|
71
|
+
return Jira::Format.ticket(ticket) +
|
72
|
+
(star ? Jira::Format.star : " ") +
|
73
|
+
Jira::Format.status(status) +
|
74
|
+
Jira::Format.summary(summary)
|
98
75
|
end
|
99
76
|
|
100
77
|
end
|
data/lib/jira/mixins.rb
CHANGED
@@ -5,57 +5,9 @@ module Jira
|
|
5
5
|
require 'json'
|
6
6
|
require 'faraday'
|
7
7
|
include Thor::Actions
|
8
|
-
include Thor::Shell
|
9
8
|
|
10
9
|
protected
|
11
10
|
|
12
|
-
#
|
13
|
-
# @return [String] JIRA endpoint
|
14
|
-
#
|
15
|
-
def jira_url
|
16
|
-
return @jira_url if !@jira_url.nil?
|
17
|
-
path = self.jira_url_path
|
18
|
-
self.validate_path!(path)
|
19
|
-
@jira_url ||= File.read(path).strip
|
20
|
-
end
|
21
|
-
|
22
|
-
#
|
23
|
-
# @return [String] JIRA authorization
|
24
|
-
#
|
25
|
-
# @return [String] username
|
26
|
-
# @return [String] password
|
27
|
-
#
|
28
|
-
def jira_auth
|
29
|
-
if !@username.nil? && !@password.nil?
|
30
|
-
return @username, @password
|
31
|
-
end
|
32
|
-
path = self.jira_auth_path
|
33
|
-
self.validate_path!(path)
|
34
|
-
@username, @password = File.read(path).strip.split(':')
|
35
|
-
return @username, @password
|
36
|
-
end
|
37
|
-
|
38
|
-
#
|
39
|
-
# @return [String] path to .jira-url file
|
40
|
-
#
|
41
|
-
def jira_url_path
|
42
|
-
@url_path ||= self.root_path + "/.jira-url"
|
43
|
-
end
|
44
|
-
|
45
|
-
#
|
46
|
-
# @return [String] path to .jira-auth file
|
47
|
-
#
|
48
|
-
def jira_auth_path
|
49
|
-
@auth_path ||= self.root_path + "/.jira-auth"
|
50
|
-
end
|
51
|
-
|
52
|
-
#
|
53
|
-
# @return [String] path to root git directory
|
54
|
-
#
|
55
|
-
def root_path
|
56
|
-
@root_path ||= ex "git rev-parse --show-toplevel"
|
57
|
-
end
|
58
|
-
|
59
11
|
#
|
60
12
|
# @return [Highline] HighLine instance for handling input
|
61
13
|
#
|
@@ -63,28 +15,5 @@ module Jira
|
|
63
15
|
@highline ||= ::HighLine.new
|
64
16
|
end
|
65
17
|
|
66
|
-
#
|
67
|
-
# Execute shell command
|
68
|
-
#
|
69
|
-
# @param command [String] command to run
|
70
|
-
# @return [String] output of the run command
|
71
|
-
#
|
72
|
-
def ex(command)
|
73
|
-
run(command, verbose:false, capture:true).strip
|
74
|
-
end
|
75
|
-
|
76
|
-
def validate_path!(path)
|
77
|
-
if !File.exists?(path)
|
78
|
-
say "Please run `jira install` before running this action..."
|
79
|
-
abort
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def discard_memoized
|
84
|
-
@username = nil
|
85
|
-
@password = nil
|
86
|
-
@jira_url = nil
|
87
|
-
end
|
88
|
-
|
89
18
|
end
|
90
19
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jira-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Darren Lin Cheng
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-11-
|
11
|
+
date: 2013-11-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -60,7 +60,10 @@ extensions: []
|
|
60
60
|
extra_rdoc_files: []
|
61
61
|
files:
|
62
62
|
- bin/jira
|
63
|
+
- lib/jira/api.rb
|
63
64
|
- lib/jira/constants.rb
|
65
|
+
- lib/jira/core.rb
|
66
|
+
- lib/jira/format.rb
|
64
67
|
- lib/jira/install.rb
|
65
68
|
- lib/jira/lookup.rb
|
66
69
|
- lib/jira/mixins.rb
|