fgi 1.0.2 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|