fgi 1.0.2 → 1.1.1
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/bin/fgi +19 -5
- data/lib/fgi.rb +15 -5
- data/lib/fgi/configuration.rb +40 -46
- data/lib/fgi/git_service.rb +211 -73
- data/lib/fgi/git_services/gitlab.rb +9 -18
- data/lib/fgi/http_requests.rb +47 -46
- data/lib/fgi/tokens.rb +18 -10
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01ff812f6d7df394c97c84fe04677f1421726d96
|
4
|
+
data.tar.gz: d8888bd13fd3f3b38ad99d3dc4ca659d438def4d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2a5e66b748bf7f182b2b0c4757e575bcad60f8a04016bbb9d91634e228415831a4bf3e5d40d0fa7982b98172c3210a01c1058e558277c7c83559dd4ac6898a3
|
7
|
+
data.tar.gz: 5a7965629498624bfd1f66a50708c9e9fc1da3ec35319e205baad5f2e976694989fafd01cbaef1ac7c1919494d53a4ca6443ab45314fe2f6cb519c51ad7231b3
|
data/bin/fgi
CHANGED
@@ -17,20 +17,32 @@ options_parser = OptionParser.new do |fgi|
|
|
17
17
|
fgi.separator ' config : run the FGI configurator.'
|
18
18
|
fgi.separator ' token [TOKEN] : define the new user token.'
|
19
19
|
fgi.separator ' new [ISSUE_NAME] : create the issue with the given name.'
|
20
|
+
fgi.separator ' fix : fix the current issue.'
|
20
21
|
fgi.separator ' ... more comming soon ...'
|
21
22
|
fgi.separator ''
|
22
23
|
fgi.separator 'Options'
|
23
24
|
|
25
|
+
fgi.on('-h', '--help', 'Display the FGI manual')
|
26
|
+
|
27
|
+
fgi.on('-v', '--version', 'Display the FGI version') do
|
28
|
+
puts "FGI #{Fgi::VERSION}"
|
29
|
+
exit!
|
30
|
+
end
|
31
|
+
|
24
32
|
fgi.on('-e', '--estimate [ESTIMATION]', 'How many time do you think you will spend on this issue ? (example: 1d13h37m05s)') do |estimate|
|
25
33
|
options[:estimate] = estimate
|
26
34
|
end
|
27
35
|
|
28
|
-
fgi.on('-l', '--later', 'Tell FGI that you
|
36
|
+
fgi.on('-l', '--later', 'Tell FGI that you only want to create an issue but not to create and switch branch.') do
|
29
37
|
options[:later] = true
|
30
38
|
end
|
31
39
|
|
32
|
-
fgi.on('-
|
33
|
-
|
40
|
+
fgi.on('-p', '--prefix [PREFIX]', 'Tell FGI that you want to add a prefix to the branch name.') do |prefix|
|
41
|
+
options[:prefix] = prefix
|
42
|
+
end
|
43
|
+
|
44
|
+
fgi.on('-m', '--fix-message [MESSAGE]', %q(Add a custom message with the basic 'Fix #ID')) do |message|
|
45
|
+
options[:fix_message] = message
|
34
46
|
end
|
35
47
|
end
|
36
48
|
options_parser.parse!
|
@@ -50,15 +62,17 @@ when 'config'
|
|
50
62
|
Fgi::Configuration.new_config
|
51
63
|
when 'new'
|
52
64
|
Fgi.configured?
|
53
|
-
if argv[1].start_with?('-')
|
65
|
+
if !argv[1].nil? && argv[1].start_with?('-')
|
54
66
|
puts %q(You can't begin your issue's title with '-')
|
55
67
|
exit!
|
56
68
|
end
|
57
69
|
title = get_full_issue_title(argv)
|
58
|
-
Fgi::GitService.
|
70
|
+
Fgi::GitService.new_issue(title: title, options: options)
|
59
71
|
when 'token'
|
60
72
|
Fgi.configured?
|
61
73
|
Fgi::Tokens.add_token(argv[1])
|
74
|
+
when 'fix'
|
75
|
+
Fgi::GitService.fix_issue(options)
|
62
76
|
else
|
63
77
|
puts options_parser
|
64
78
|
end
|
data/lib/fgi.rb
CHANGED
@@ -6,6 +6,7 @@ module Fgi
|
|
6
6
|
require 'json'
|
7
7
|
require 'yaml'
|
8
8
|
require 'uri'
|
9
|
+
require 'cgi'
|
9
10
|
|
10
11
|
require_relative 'fgi/git_services/gitlab'
|
11
12
|
require_relative 'fgi/http_requests'
|
@@ -13,19 +14,28 @@ module Fgi
|
|
13
14
|
require_relative 'fgi/configuration'
|
14
15
|
require_relative 'fgi/git_service'
|
15
16
|
|
16
|
-
|
17
|
+
VERSION = '1.1.1'.freeze
|
18
|
+
|
19
|
+
# Add FGI user's current issues to the gitignore
|
20
|
+
if `cat .gitignore | grep '.current_issues.fgi.yml'`.empty?
|
21
|
+
File.open('.gitignore', 'a') { |f| f.write("\n.current_issues.fgi.yml\n") }
|
22
|
+
end
|
23
|
+
|
24
|
+
ISSUES = YAML.load_file('.current_issues.fgi.yml') if File.exist?('.current_issues.fgi.yml')
|
25
|
+
# Define const variables if fgi config files exists
|
17
26
|
# otherwise ask for configuration
|
18
|
-
if File.
|
27
|
+
if File.exist?('.config.fgi.yml')
|
19
28
|
CONFIG = YAML.load_file('.config.fgi.yml')
|
20
29
|
git_service = CONFIG[:git_service_class].new
|
21
|
-
if File.
|
22
|
-
TOKEN = YAML.load_file("#{Dir.home}/.tokens.fgi.yml")[git_service.to_sym]
|
30
|
+
if File.exist?("#{Dir.home}/.tokens.fgi.yml")
|
31
|
+
TOKEN = YAML.load_file("#{Dir.home}/.tokens.fgi.yml")[git_service.to_sym][CONFIG[:url]]
|
23
32
|
end
|
24
33
|
end
|
25
34
|
|
26
35
|
def self.configured?
|
27
|
-
return if File.
|
36
|
+
return if File.exist?('.config.fgi.yml')
|
28
37
|
puts "\nThere is no FGI configuration file on this project. Please run 'fgi config'.\n\n"
|
29
38
|
exit!
|
30
39
|
end
|
40
|
+
|
31
41
|
end
|
data/lib/fgi/configuration.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
module Fgi
|
3
3
|
class Configuration
|
4
4
|
class << self
|
5
|
+
|
5
6
|
include HttpRequests
|
6
7
|
|
7
8
|
# Launch the process to create the fresh project fgi config file
|
@@ -21,7 +22,6 @@ module Fgi
|
|
21
22
|
# INITIALIZERS #
|
22
23
|
# -------------------------- #
|
23
24
|
|
24
|
-
|
25
25
|
# The hash that will contain the project's fgi configuration to save as yml
|
26
26
|
# It will contain :
|
27
27
|
# :url
|
@@ -38,18 +38,17 @@ module Fgi
|
|
38
38
|
# TODO - HARD REFECTO NEEDED HERE...
|
39
39
|
git_service = config[:git_service_class].new(config: config)
|
40
40
|
config[:git_service] = git_service.to_sym
|
41
|
-
user_token = save_user_token(git_service)
|
41
|
+
user_token = save_user_token(config: config, git_service: git_service)
|
42
42
|
project_name_and_id = define_project_name_and_id(git_service, user_token)
|
43
43
|
config = config.merge(project_name_and_id)
|
44
44
|
git_service = config[:git_service_class].new(config: config)
|
45
45
|
config[:default_branch] = define_default_branch(git_service, user_token)
|
46
46
|
|
47
|
-
|
48
47
|
# -------------------------- #
|
49
48
|
# CREATORS #
|
50
49
|
# -------------------------- #
|
51
50
|
|
52
|
-
Fgi::Tokens.create_user_tokens_file(config
|
51
|
+
Fgi::Tokens.create_user_tokens_file(config: config, git_service: git_service, token: user_token)
|
53
52
|
create_fgi_config_file(config)
|
54
53
|
end
|
55
54
|
|
@@ -57,21 +56,19 @@ module Fgi
|
|
57
56
|
|
58
57
|
# Check if we are in a git repository. Exit FGI if not.
|
59
58
|
def git_directory?
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
59
|
+
return if Dir.exist?('.git')
|
60
|
+
puts 'You are not in a git project repository.'
|
61
|
+
exit!
|
64
62
|
end
|
65
63
|
|
66
64
|
# Check if FGI has already been configured. Exit FGI if not.
|
67
65
|
def already_configured?
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
66
|
+
return unless File.exist?('.config.fgi.yml')
|
67
|
+
puts 'There is already a FGI config on this project.'
|
68
|
+
exit!
|
72
69
|
end
|
73
70
|
|
74
|
-
#
|
71
|
+
# Ask the user to shoose the project's Git service
|
75
72
|
# @return [Class] the project's Git service class
|
76
73
|
def define_git_service
|
77
74
|
puts "\nPlease insert the number of the used Git service :"
|
@@ -81,9 +78,9 @@ module Fgi
|
|
81
78
|
git_services = Fgi::GitService.services
|
82
79
|
# Display theses services to let the user choose the project's one
|
83
80
|
git_services.each_with_index do |service, index|
|
84
|
-
puts "#{index+1} : #{service.capitalize}"
|
81
|
+
puts "#{index + 1} : #{service.capitalize}"
|
85
82
|
end
|
86
|
-
puts
|
83
|
+
puts '... More soon ...'
|
87
84
|
|
88
85
|
begin
|
89
86
|
input = STDIN.gets.chomp
|
@@ -91,15 +88,15 @@ module Fgi
|
|
91
88
|
# Convert the string input to an integer
|
92
89
|
input = input.to_i
|
93
90
|
# If the input isn't out of range...
|
94
|
-
if (1..git_services.count).
|
91
|
+
if (1..git_services.count).cover?(input)
|
95
92
|
# Set a variable with the Git service name for displays
|
96
|
-
@git_service = git_services[input-1].capitalize
|
93
|
+
@git_service = git_services[input - 1].capitalize
|
97
94
|
Fgi::GitServices.const_get(@git_service)
|
98
95
|
else
|
99
96
|
puts "\nSorry, the option is out of range. Try again :"
|
100
97
|
define_git_service
|
101
98
|
end
|
102
|
-
rescue Interrupt
|
99
|
+
rescue Interrupt
|
103
100
|
exit!
|
104
101
|
end
|
105
102
|
end
|
@@ -116,18 +113,18 @@ module Fgi
|
|
116
113
|
exit! if input == 'quit'
|
117
114
|
# force scheme if not specified
|
118
115
|
# TODO - Find a way to clear this... Find the correct scheme.
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
116
|
+
if input.start_with?('gitlab.com')
|
117
|
+
input = "https://#{input}"
|
118
|
+
elsif !input.start_with?('http://', 'https://')
|
119
|
+
input = "http://#{input}"
|
120
|
+
end
|
124
121
|
# Call the entered url to know if it exist or not.
|
125
122
|
# If not, would raise an exception
|
126
123
|
get(url: input)
|
127
124
|
input
|
128
|
-
rescue Interrupt
|
125
|
+
rescue Interrupt
|
129
126
|
exit!
|
130
|
-
rescue Exception
|
127
|
+
rescue Exception
|
131
128
|
puts "\nOops, seems to be a bad url. Try again or quit (quit)"
|
132
129
|
save_git_url
|
133
130
|
end
|
@@ -136,10 +133,9 @@ module Fgi
|
|
136
133
|
# Ask for the user for his Git service token
|
137
134
|
# @param git_service [Class] the current project's git service class
|
138
135
|
# @return [String] the user Git service token
|
139
|
-
def save_user_token(git_service)
|
140
|
-
token = Fgi::Tokens.get_token(git_service)
|
136
|
+
def save_user_token(config:, git_service:)
|
137
|
+
token = Fgi::Tokens.get_token(config: config, git_service: git_service)
|
141
138
|
return token unless token.nil?
|
142
|
-
save_user_token(git_service)
|
143
139
|
end
|
144
140
|
|
145
141
|
# Ask the user to search for the project and to select the correct one.
|
@@ -161,7 +157,7 @@ module Fgi
|
|
161
157
|
if response[:status] == '200' && !response[:body].empty?
|
162
158
|
puts "\nFound #{response[:body].count} match(es):"
|
163
159
|
response[:body].each_with_index do |project, index|
|
164
|
-
puts "#{index+1} - #{project['name_with_namespace']}"
|
160
|
+
puts "#{index + 1} - #{project['name_with_namespace']}"
|
165
161
|
end
|
166
162
|
|
167
163
|
puts "\nPlease insert the number of the current project :"
|
@@ -174,10 +170,10 @@ module Fgi
|
|
174
170
|
|
175
171
|
else
|
176
172
|
puts "\nOops, we couldn't find a project called #{input}. Try again or quit (quit) :"
|
177
|
-
puts '-------------------------------------------------------------------'+('-'*input.length) # Don't be upset, i'm a perfectionist <3
|
173
|
+
puts '-------------------------------------------------------------------' + ('-' * input.length) # Don't be upset, i'm a perfectionist <3
|
178
174
|
define_project_name_and_id(git_service, user_token)
|
179
175
|
end
|
180
|
-
rescue Interrupt
|
176
|
+
rescue Interrupt
|
181
177
|
exit!
|
182
178
|
end
|
183
179
|
end
|
@@ -186,23 +182,21 @@ module Fgi
|
|
186
182
|
puts "\nPlease define the default project branch :"
|
187
183
|
puts '------------------------------------------'
|
188
184
|
|
189
|
-
url =
|
185
|
+
url = git_service.routes[:branches]
|
190
186
|
response = get(url: url, headers: { git_service.token_header => user_token })
|
187
|
+
return unless response[:status] == '200' && !response[:body].empty?
|
191
188
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
puts "#{index+1} - #{branch['name']}"
|
196
|
-
end
|
197
|
-
|
198
|
-
puts "\nPlease insert the number of the default project branch :"
|
199
|
-
puts '--------------------------------------------------------'
|
200
|
-
input = validate_choice(response[:body])
|
201
|
-
response[:body][input - 1]['name']
|
202
|
-
|
203
|
-
rescue Interrupt => int
|
204
|
-
exit!
|
189
|
+
begin
|
190
|
+
response[:body].each_with_index do |branch, index|
|
191
|
+
puts "#{index + 1} - #{branch['name']}"
|
205
192
|
end
|
193
|
+
|
194
|
+
puts "\nPlease insert the number of the default project branch :"
|
195
|
+
puts '--------------------------------------------------------'
|
196
|
+
input = validate_choice(response[:body])
|
197
|
+
response[:body][input - 1]['name']
|
198
|
+
rescue Interrupt
|
199
|
+
exit!
|
206
200
|
end
|
207
201
|
end
|
208
202
|
|
@@ -210,7 +204,7 @@ module Fgi
|
|
210
204
|
input = STDIN.gets.chomp
|
211
205
|
exit! if input == 'quit'
|
212
206
|
input = input.to_i
|
213
|
-
if (1..response_body.count).
|
207
|
+
if (1..response_body.count).cover?(input)
|
214
208
|
input
|
215
209
|
else
|
216
210
|
puts "\nSorry, the option is out of range. Try again :"
|
data/lib/fgi/git_service.rb
CHANGED
@@ -2,120 +2,227 @@
|
|
2
2
|
module Fgi
|
3
3
|
class GitService
|
4
4
|
class << self
|
5
|
+
|
5
6
|
include HttpRequests
|
6
7
|
|
8
|
+
# @return [Array<String>] an array containing all the git services available.
|
7
9
|
def services
|
8
10
|
services = []
|
9
11
|
Dir.entries("#{File.dirname(__FILE__)}/git_services").each do |service|
|
10
|
-
services << service.gsub(/.rb/, '').to_sym unless %w
|
12
|
+
services << service.gsub(/.rb/, '').to_sym unless %w[. ..].include?(service)
|
11
13
|
end
|
12
14
|
services
|
13
15
|
end
|
14
16
|
|
15
|
-
|
17
|
+
# All the process initiated by the issue creation
|
18
|
+
# => Create issue
|
19
|
+
# => Create a new branch from the default one
|
20
|
+
# => Set the issue estimation time
|
21
|
+
# @param title [String] the issue title
|
22
|
+
# @param options [Hash] the options given by the user in the command line
|
23
|
+
def new_issue(title:, options: {})
|
16
24
|
git_service = CONFIG[:git_service_class].new
|
17
25
|
title = get_issue_title if title.nil?
|
18
|
-
|
19
|
-
|
20
|
-
headers = { git_service.token_header => TOKEN, 'Content-Type' => 'application/json' }
|
21
|
-
url_with_querystring = "#{git_service.routes[:issues]}?title=#{URI.encode(title)}&description=#{URI.encode(description)}"
|
22
|
-
|
23
|
-
response = post(url: url_with_querystring, headers: headers)
|
24
|
-
response_body = JSON.parse(response[:body])
|
25
|
-
|
26
|
-
post_issue_display(response_body)
|
26
|
+
response = create_issue(title: title, git_service: git_service)
|
27
27
|
|
28
28
|
if CONFIG[:default_branch].nil?
|
29
29
|
puts "\n/!\\ FGI IS NOT UP-TO-DATE /!\\"
|
30
30
|
puts 'We are not able to create and switch you to the new branch.'
|
31
31
|
puts 'Delete .config.fgi.yml and reconfigure fgi by running `fgi config`'
|
32
|
-
|
33
|
-
|
32
|
+
elsif !response['iid'].nil?
|
33
|
+
branch_name = snakify(title)
|
34
|
+
branch_name = "#{options[:prefix]}/#{branch_name}" unless options[:prefix].nil?
|
35
|
+
save_issue(branch: branch_name, id: response['iid'], title: response['title'].tr("'", ' '))
|
36
|
+
create_branch(branch_name) unless options[:later]
|
37
|
+
set_issue_estimation(
|
38
|
+
issue_id: response['iid'],
|
39
|
+
estimation: options[:estimate],
|
40
|
+
git_service: git_service
|
41
|
+
)
|
34
42
|
end
|
43
|
+
end
|
35
44
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
+
# All the process initiated by the issue fix
|
46
|
+
# => Commiting with a 'Fix #<id>' message
|
47
|
+
# => Pushing the current branch to the remote repo
|
48
|
+
# => Return to the default project branch
|
49
|
+
# @param options [Hash] the options given by the user in the command line
|
50
|
+
def fix_issue(options)
|
51
|
+
current_branch = `git branch | grep '*'`.gsub('* ', '').chomp
|
52
|
+
if ISSUES[current_branch].nil?
|
53
|
+
puts "We could not find any issues to fix on your current branch (#{current_branch})."
|
54
|
+
exit!
|
55
|
+
end
|
56
|
+
git_remote = ask_for_remote
|
57
|
+
`git add .`
|
58
|
+
puts "Are you sure to want to close the issue #{ISSUES[current_branch][:title]} ?"
|
59
|
+
begin
|
60
|
+
input = STDIN.gets.chomp
|
61
|
+
if %w[y yes].include?(input)
|
62
|
+
commit_message = "Fix ##{ISSUES[current_branch][:id]}"
|
63
|
+
commit_message += " - #{options[:fix_message]}" unless options[:fix_message].nil?
|
64
|
+
`git commit -a --allow-empty -m '#{commit_message}'`
|
65
|
+
`git push #{git_remote} HEAD`
|
66
|
+
`git checkout #{CONFIG[:default_branch]}` # Be sure to be on the default branch.
|
67
|
+
remove_issue(current_branch)
|
68
|
+
puts "Congrat's ! You're now back to work on the default branch (#{CONFIG[:default_branch]})"
|
45
69
|
end
|
46
|
-
|
47
|
-
|
70
|
+
rescue Interrupt
|
71
|
+
puts %q"Why did you killed me ? :'("
|
72
|
+
exit!
|
48
73
|
end
|
49
74
|
end
|
50
75
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
puts
|
72
|
-
|
76
|
+
private
|
77
|
+
|
78
|
+
def ask_for_remote
|
79
|
+
remotes = `git remote`.split("\n")
|
80
|
+
return remotes.first if remotes.count == 1
|
81
|
+
|
82
|
+
puts "\nHere are your remotes :"
|
83
|
+
remotes.each_with_index do |remote, index|
|
84
|
+
puts "#{index + 1} - #{remote}"
|
85
|
+
end
|
86
|
+
|
87
|
+
begin
|
88
|
+
puts "\nPlease insert the number of the remote to use :"
|
89
|
+
puts '-----------------------------------------------'
|
90
|
+
input = STDIN.gets.chomp
|
91
|
+
exit! if input == 'quit'
|
92
|
+
input = input.to_i
|
93
|
+
if (1..remotes.count).cover?(input)
|
94
|
+
remotes[input-1]
|
95
|
+
else
|
96
|
+
puts "\nSorry, the option is out of range. Try again :"
|
97
|
+
ask_for_remote
|
73
98
|
end
|
99
|
+
rescue Interrupt
|
100
|
+
puts %q"Why did you killed me ? :'("
|
101
|
+
exit!
|
74
102
|
end
|
75
|
-
%x(git checkout #{CONFIG[:default_branch]}) # Be sure to be on the default branch.
|
76
|
-
from = %x(git branch | grep '*').gsub('* ', '').chomp
|
77
|
-
%x(git pull origin HEAD) # Be sure to get the remote changes locally.
|
78
|
-
%x(git checkout -b #{branch_name}) # Create the new branch.
|
79
|
-
to = %x(git branch | grep '*').gsub('* ', '').chomp
|
80
|
-
puts "\nYou are now working on branch #{to} created from #{from} !"
|
81
103
|
end
|
82
104
|
|
83
|
-
|
105
|
+
# Save the current user's FGI created issue in the gitignored file 'current_issue.fgi.yml'
|
106
|
+
# @param id [Integer] the current issue id
|
107
|
+
# @param title [String] the current issue name
|
108
|
+
def save_issue(branch:, id:, title:)
|
109
|
+
if File.exist?('.current_issues.fgi.yml')
|
110
|
+
issues = YAML.load_file('.current_issues.fgi.yml')
|
111
|
+
issues[branch] = { id: id, title: title }
|
112
|
+
else
|
113
|
+
issues = {
|
114
|
+
branch => {
|
115
|
+
id: id,
|
116
|
+
title: title
|
117
|
+
}
|
118
|
+
}
|
119
|
+
end
|
120
|
+
# Shouldn't we define some access restrictions on this file ?
|
121
|
+
File.open('.current_issues.fgi.yml', 'w') { |f| f.write(issues.to_yaml) }
|
122
|
+
end
|
123
|
+
|
124
|
+
def remove_issue(branch)
|
125
|
+
issues = YAML.load_file('.current_issues.fgi.yml')
|
126
|
+
issues.delete(branch) unless ISSUES[branch].nil?
|
127
|
+
File.open('.current_issues.fgi.yml', 'w') { |f| f.write(issues.to_yaml) }
|
128
|
+
end
|
129
|
+
|
130
|
+
# TODO, Make sure it works for all git services
|
131
|
+
# The method to set the estimation time to resolve the issue
|
132
|
+
# @param issue_id [Integer] the issue id to set its estimation time
|
133
|
+
# @param estimation [String] the estimation time given by the user
|
134
|
+
# @param git_service [Class] the git service class to use for this project
|
135
|
+
def set_issue_estimation(issue_id:, estimation:, git_service:)
|
136
|
+
return if estimation.nil?
|
137
|
+
# Since GitLab version isn't up to date, we should be able
|
138
|
+
# to add estimations in issues comments (/estimate)
|
139
|
+
url_with_querystring = "#{git_service.routes[:issues]}/#{issue_id}/time_estimate?duration=#{estimation}"
|
140
|
+
response = post(url: url_with_querystring, headers: headers)
|
141
|
+
# GitLab sucks sometimes... This API is an example
|
142
|
+
begin
|
143
|
+
response_body = JSON.parse(response[:body])
|
144
|
+
rescue Exception
|
145
|
+
response_body = response[:body]
|
146
|
+
end
|
147
|
+
post_estimation_display(response_body['human_time_estimate'], estimation)
|
148
|
+
end
|
149
|
+
|
150
|
+
# TODO, Make sure it works for all git services
|
151
|
+
# The method used to create issues
|
152
|
+
# @param title [String] the issue title
|
153
|
+
# @param git_service [Class] the git service class to use for this project
|
154
|
+
# @return [Boolean] true if the issue has been created, false otherwise
|
155
|
+
def create_issue(title:, git_service:)
|
156
|
+
description = get_issue_description
|
157
|
+
|
158
|
+
headers = { git_service.token_header => TOKEN, 'Content-Type' => 'application/json' }
|
159
|
+
url_with_querystring = "#{git_service.routes[:issues]}?title=#{CGI.escape(title)}&description=#{CGI.escape(description)}"
|
160
|
+
|
161
|
+
response = post(url: url_with_querystring, headers: headers)
|
162
|
+
response_body = JSON.parse(response[:body])
|
163
|
+
|
164
|
+
post_issue_display(response_body['iid'])
|
165
|
+
response_body
|
166
|
+
end
|
84
167
|
|
168
|
+
# The method used to create branches
|
169
|
+
# @param name [String] the branch name
|
170
|
+
def create_branch(name)
|
171
|
+
check_status
|
172
|
+
git_remote = ask_for_remote
|
173
|
+
`git checkout #{CONFIG[:default_branch]}` # Be sure to be on the default branch.
|
174
|
+
from = `git branch | grep '*'`.gsub('* ', '').chomp
|
175
|
+
`git pull #{git_remote} HEAD` # Be sure to get the remote changes locally.
|
176
|
+
`git checkout -b #{name}` # Create the new branch.
|
177
|
+
to = `git branch | grep '*'`.gsub('* ', '').chomp
|
178
|
+
puts "\nYou are now working on branch #{to} created from #{from} !"
|
179
|
+
end
|
180
|
+
|
181
|
+
# The method used to get the issue description
|
182
|
+
# @return [String] the issue description
|
85
183
|
def get_issue_description
|
86
184
|
puts "\nWrite your issue description right bellow (save and quit with CTRL+D) :"
|
87
185
|
puts "-----------------------------------------------------------------------\n\n"
|
88
186
|
begin
|
89
187
|
STDIN.read
|
90
|
-
rescue Interrupt
|
188
|
+
rescue Interrupt
|
91
189
|
puts %q"Why did you killed me ? :'("
|
92
190
|
exit!
|
93
191
|
end
|
94
192
|
end
|
95
193
|
|
194
|
+
# The method used to get the issue title if not given the first time
|
195
|
+
# @return [String] the issue title
|
96
196
|
def get_issue_title
|
97
|
-
puts "\nWhat
|
197
|
+
puts "\nWhat is your issue title :"
|
98
198
|
puts "--------------------------\n\n"
|
99
199
|
begin
|
100
200
|
STDIN.gets.chomp
|
101
|
-
rescue Interrupt
|
201
|
+
rescue Interrupt
|
102
202
|
puts %q"Why did you killed me ? :'("
|
103
203
|
exit!
|
104
204
|
end
|
105
205
|
end
|
106
206
|
|
107
|
-
|
108
|
-
|
207
|
+
# The display method to let the user know
|
208
|
+
# if the issue has correctly been created
|
209
|
+
# @param issue_id [Integer] the id of the created issue
|
210
|
+
def post_issue_display(issue_id)
|
211
|
+
if !issue_id.nil?
|
109
212
|
puts 'Your issue has been successfully created.'
|
110
213
|
puts 'To view it, please follow the link bellow :'
|
111
|
-
puts "\n#{CONFIG[:url]}/#{CONFIG[:project_slug]}/issues/#{
|
214
|
+
puts "\n#{CONFIG[:url]}/#{CONFIG[:project_slug]}/issues/#{issue_id}"
|
112
215
|
else
|
113
216
|
puts %q(Your issue couldn't be created. Check your FGI configuration.)
|
114
217
|
end
|
115
218
|
end
|
116
219
|
|
117
|
-
|
118
|
-
|
220
|
+
# The display method to let the user know if the
|
221
|
+
# estimation time has correctly been set on the issue
|
222
|
+
# @param response_estimation [String] the estimation time response from the git service
|
223
|
+
# @param estimation [String] the estimation time given by the user
|
224
|
+
def post_estimation_display(response_estimation, estimation)
|
225
|
+
if response_estimation.nil?
|
119
226
|
puts "\nWe weren't able to save your estimation."
|
120
227
|
puts "You'll have to do it manually on #{CONFIG[:git_service].capitalize}."
|
121
228
|
else
|
@@ -123,28 +230,59 @@ module Fgi
|
|
123
230
|
end
|
124
231
|
end
|
125
232
|
|
233
|
+
# The method used to commit the user's local changes
|
126
234
|
def commit_changes
|
127
235
|
puts 'Enter your commit message :'
|
128
236
|
commit_message = STDIN.gets.chomp
|
129
|
-
|
130
|
-
|
237
|
+
`git add .`
|
238
|
+
`git commit -am '#{commit_message}'`
|
131
239
|
puts 'Your changes have been commited !'
|
132
240
|
end
|
133
241
|
|
242
|
+
# The method used to stash the user's local changes
|
134
243
|
def stash_changes
|
135
|
-
|
136
|
-
|
244
|
+
`git add .`
|
245
|
+
`git stash`
|
137
246
|
puts "\nYour changes have been stashed."
|
138
|
-
puts "We will let you manually
|
247
|
+
puts "We will let you manually git stash pop to get your work back if needed.\n"
|
248
|
+
end
|
249
|
+
|
250
|
+
# The method used to check if there are local changes and to
|
251
|
+
# ask the user if he want to commit or stash theses changes
|
252
|
+
def check_status
|
253
|
+
return if `git status -s`.empty?
|
254
|
+
begin
|
255
|
+
puts "\nThere are unsaved changes on your current branch."
|
256
|
+
puts 'Do you want to see them ? (y/n)'
|
257
|
+
puts '-------------------------------'
|
258
|
+
input = STDIN.gets.chomp
|
259
|
+
system('git diff') if %w[y yes].include?(input)
|
260
|
+
|
261
|
+
puts "\nDo you want to COMMIT theses changes ? (y/n)"
|
262
|
+
puts '--------------------------------------------'
|
263
|
+
input = STDIN.gets.chomp
|
264
|
+
if %w[y yes].include?(input)
|
265
|
+
commit_changes
|
266
|
+
else
|
267
|
+
stash_changes
|
268
|
+
end
|
269
|
+
rescue Interrupt
|
270
|
+
puts %q"Why did you killed me ? :'("
|
271
|
+
exit!
|
272
|
+
end
|
139
273
|
end
|
140
274
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
275
|
+
# The method used to snakify strings
|
276
|
+
# @param string [String] the string to snakify
|
277
|
+
# @return [String] the snakified string
|
278
|
+
def snakify(string)
|
279
|
+
string.gsub(/::/, '/')
|
280
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
281
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
282
|
+
.tr('-', '_')
|
283
|
+
.tr(' ', '_')
|
284
|
+
.tr("'", '_')
|
285
|
+
.downcase
|
148
286
|
end
|
149
287
|
|
150
288
|
end
|
@@ -3,27 +3,18 @@ module Fgi
|
|
3
3
|
module GitServices
|
4
4
|
class Gitlab
|
5
5
|
|
6
|
+
attr_reader :version, :token_header, :routes
|
7
|
+
|
6
8
|
def initialize(config: CONFIG)
|
7
|
-
@version
|
9
|
+
@version = 'v4'
|
10
|
+
@main_url = "#{config[:url]}/api/#{@version}"
|
8
11
|
@token_header = 'PRIVATE-TOKEN'
|
9
12
|
@routes = {
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
def version
|
18
|
-
@version
|
19
|
-
end
|
20
|
-
|
21
|
-
def token_header
|
22
|
-
@token_header
|
23
|
-
end
|
24
|
-
|
25
|
-
def routes
|
26
|
-
@routes
|
13
|
+
projects: "#{@main_url}/projects",
|
14
|
+
search_projects: "#{@main_url}/projects?search=",
|
15
|
+
issues: "#{@main_url}/projects/#{config[:project_id]}/issues",
|
16
|
+
branches: "#{@main_url}/projects/#{config[:project_id]}/repository/branches"
|
17
|
+
}
|
27
18
|
end
|
28
19
|
|
29
20
|
def to_sym
|
data/lib/fgi/http_requests.rb
CHANGED
@@ -2,55 +2,56 @@
|
|
2
2
|
module Fgi
|
3
3
|
module HttpRequests
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
# Generic method to GET requests
|
6
|
+
# @param url [String] the given Git service API url for GET request
|
7
|
+
# @param headers [Hash] the headers to set for the request
|
8
|
+
# @return [String] the received response from the Git service API
|
9
|
+
def get(url:, headers: nil)
|
10
|
+
http_request(verb: :get, url: url, headers: headers)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Generic method to POST requests
|
14
|
+
# @param url [String] the given Git service API url for POST request
|
15
|
+
# @param headers [Hash] the headers to set for the request
|
16
|
+
# @param body [Hash] the body to set for the request
|
17
|
+
# @return [String] the received response from the Git service API
|
18
|
+
def post(url:, headers: nil, body: nil)
|
19
|
+
http_request(verb: :post, url: url, headers: headers, body: body)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Generic method for HTTP requests
|
25
|
+
# @param url [String] the given Git service API url for a HTTP request
|
26
|
+
# @param headers [Hash] the headers to set for the request
|
27
|
+
# @param body [Hash] the body to set for the request
|
28
|
+
# @return [String] the received response from the Git service API
|
29
|
+
def http_request(verb:, url:, headers: nil, body: nil)
|
30
|
+
is_https = url.start_with?('https')
|
31
|
+
uri = URI.parse(url)
|
32
|
+
|
33
|
+
req = case verb
|
34
|
+
when :get
|
35
|
+
Net::HTTP::Get.new(url)
|
36
|
+
when :post
|
37
|
+
Net::HTTP::Post.new(url)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Set headers if given
|
41
|
+
headers.each { |k, v| req[k] = v } unless headers.nil?
|
42
|
+
# Set body if given
|
43
|
+
req.body = body.to_json unless body.nil?
|
44
|
+
|
45
|
+
res = Net::HTTP.start(uri.host, uri.port, use_ssl: is_https) do |http|
|
46
|
+
http.request(req)
|
11
47
|
end
|
12
48
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
# @return [String] the received response from the Git service API
|
18
|
-
def post(url:, headers: nil, body: nil)
|
19
|
-
http_request(verb: :post, url: url, headers: headers)
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
# Generic method for HTTP requests
|
25
|
-
# @param url [String] the given Git service API url for a HTTP request
|
26
|
-
# @param headers [Hash] the headers to set for the request
|
27
|
-
# @param body [Hash] the body to set for the request
|
28
|
-
# @return [String] the received response from the Git service API
|
29
|
-
def http_request(verb:, url:, headers: nil, body: nil)
|
30
|
-
is_https = url.start_with?('https')
|
31
|
-
uri = URI.parse(url)
|
32
|
-
req = case verb
|
33
|
-
when :get
|
34
|
-
Net::HTTP::Get.new(url)
|
35
|
-
when :post
|
36
|
-
Net::HTTP::Post.new(url)
|
37
|
-
end
|
38
|
-
|
39
|
-
# Set headers if given
|
40
|
-
headers.each { |k, v| req[k] = v } unless headers.nil?
|
41
|
-
# Set body if given
|
42
|
-
req.body = body.to_json unless body.nil?
|
43
|
-
|
44
|
-
res = Net::HTTP.start(uri.host, uri.port, use_ssl: is_https) do |http|
|
45
|
-
http.request(req)
|
46
|
-
end
|
47
|
-
|
48
|
-
if res.code == '200'
|
49
|
-
{ status: '200', body: JSON.parse(res.body) }
|
50
|
-
else
|
51
|
-
{ status: res.code, body: res.body }
|
52
|
-
end
|
49
|
+
if res.code == '200'
|
50
|
+
{ status: '200', body: JSON.parse(res.body) }
|
51
|
+
else
|
52
|
+
{ status: res.code, body: res.body }
|
53
53
|
end
|
54
|
+
end
|
54
55
|
|
55
56
|
end
|
56
57
|
end
|
data/lib/fgi/tokens.rb
CHANGED
@@ -2,16 +2,21 @@
|
|
2
2
|
module Fgi
|
3
3
|
class Tokens
|
4
4
|
class << self
|
5
|
+
|
5
6
|
include HttpRequests
|
6
7
|
|
7
8
|
# @param git_service_name [String] the git service to associate a token to
|
8
9
|
# @param token [String] the token to associate to the git service
|
9
|
-
def create_user_tokens_file(git_service
|
10
|
-
if File.
|
10
|
+
def create_user_tokens_file(config:, git_service:, token:)
|
11
|
+
if File.exist?("#{Dir.home}/.tokens.fgi.yml")
|
11
12
|
tokens = YAML.load_file("#{Dir.home}/.tokens.fgi.yml")
|
12
|
-
tokens[git_service] = token
|
13
|
+
tokens[git_service.to_sym] = { config[:url] => token }
|
13
14
|
else
|
14
|
-
tokens = {
|
15
|
+
tokens = {
|
16
|
+
git_service => {
|
17
|
+
config[:url] => token
|
18
|
+
}
|
19
|
+
}
|
15
20
|
end
|
16
21
|
# Shouldn't we define some access restrictions on this file ?
|
17
22
|
File.open("#{Dir.home}/.tokens.fgi.yml", 'w') { |f| f.write(tokens.to_yaml) }
|
@@ -34,12 +39,12 @@ module Fgi
|
|
34
39
|
# @param git_service [Class] the current project's git service class
|
35
40
|
# @return [String] the current token associated to the project's git service
|
36
41
|
# @return [NilClass] if there is no token associated to the project's git service
|
37
|
-
def get_token(git_service)
|
38
|
-
if File.
|
42
|
+
def get_token(config:, git_service:)
|
43
|
+
if File.exist?("#{Dir.home}/.tokens.fgi.yml")
|
39
44
|
tokens = YAML.load_file("#{Dir.home}/.tokens.fgi.yml")
|
40
|
-
tokens[git_service.to_sym]
|
45
|
+
tokens[git_service.to_sym][config[:url]]
|
41
46
|
else
|
42
|
-
puts "\nPlease enter your #{git_service
|
47
|
+
puts "\nPlease enter your #{git_service} token :"
|
43
48
|
puts '(use `fgi --help` to check how to get your token)'
|
44
49
|
puts '-------------------------------------------------'
|
45
50
|
begin
|
@@ -47,7 +52,7 @@ module Fgi
|
|
47
52
|
exit! if token == 'quit'
|
48
53
|
return token if token_valid?(git_service, token)
|
49
54
|
nil
|
50
|
-
rescue Interrupt
|
55
|
+
rescue Interrupt
|
51
56
|
exit!
|
52
57
|
end
|
53
58
|
end
|
@@ -57,7 +62,10 @@ module Fgi
|
|
57
62
|
# @param token [String] the token to check the validity
|
58
63
|
# @return [Boolean] true if the token is valid, false otherwise
|
59
64
|
def token_valid?(git_service, token)
|
60
|
-
response = get(
|
65
|
+
response = get(
|
66
|
+
url: git_service.routes[:projects],
|
67
|
+
headers: { git_service.token_header => token }
|
68
|
+
)
|
61
69
|
response[:status] == '200'
|
62
70
|
end
|
63
71
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fgi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julien Philibin
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-
|
12
|
+
date: 2017-10-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|