gb 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gb.yml +12 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +94 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/gb +7 -0
- data/exe/glb +7 -0
- data/gb.gemspec +39 -0
- data/lib/Command.rb +75 -0
- data/lib/commands/create.rb +67 -0
- data/lib/commands/create_tag.rb +104 -0
- data/lib/commands/delete_tag.rb +98 -0
- data/lib/commands/forall.rb +53 -0
- data/lib/commands/init.rb +36 -0
- data/lib/commands/merge.rb +249 -0
- data/lib/commands/review.rb +260 -0
- data/lib/commands/start.rb +116 -0
- data/lib/commands/status.rb +78 -0
- data/lib/commands/sync.rb +45 -0
- data/lib/commands/workspace.rb +38 -0
- data/lib/config/gb_config.rb +73 -0
- data/lib/config/work_space_config.rb +25 -0
- data/lib/ext/git_ext.rb +120 -0
- data/lib/ext/gitlab_ext.rb +26 -0
- data/lib/gb.rb +8 -0
- data/lib/gb/version.rb +3 -0
- data/lib/sub_command.rb +139 -0
- metadata +176 -0
@@ -0,0 +1,260 @@
|
|
1
|
+
require 'sub_command'
|
2
|
+
require 'ext/gitlab_ext'
|
3
|
+
|
4
|
+
module Gb
|
5
|
+
class Review < SubCommand
|
6
|
+
|
7
|
+
self.summary = 'push工作分支到远程服务器,并创建merge request.'
|
8
|
+
|
9
|
+
self.description = <<-DESC
|
10
|
+
push工作分支到远程服务器,并创建merge request.
|
11
|
+
DESC
|
12
|
+
|
13
|
+
# self.arguments = [
|
14
|
+
# CLAide::Argument.new('working_branch', false, false),
|
15
|
+
# CLAide::Argument.new('remote_branch', false, false),
|
16
|
+
# ]
|
17
|
+
|
18
|
+
def self.options
|
19
|
+
[
|
20
|
+
["--assignee=[user name]", "指定review用户名称"],
|
21
|
+
["--title", "merge request标题"],
|
22
|
+
["--show-diff", "review前是否显示变更"],
|
23
|
+
].concat(super)
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(argv)
|
27
|
+
# @working_branch = argv.shift_argument
|
28
|
+
# @remote_branch = argv.shift_argument
|
29
|
+
@assignee = argv.option('assignee')
|
30
|
+
@title = argv.option('title')
|
31
|
+
@show_diff = argv.flag?('show-diff')
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate!
|
36
|
+
super
|
37
|
+
# if @working_branch.nil?
|
38
|
+
# help! 'working_branch is required.'
|
39
|
+
# end
|
40
|
+
# if @remote_branch.nil?
|
41
|
+
# help! 'remote_branch is required.'
|
42
|
+
# end
|
43
|
+
# if @assignee.nil?
|
44
|
+
# help! 'assignee is required.'
|
45
|
+
# end
|
46
|
+
end
|
47
|
+
|
48
|
+
def run_in_workspace
|
49
|
+
|
50
|
+
@working_branch = self.workspace_config.workspace_branch
|
51
|
+
@remote_branch = self.workspace_config.remote_branch
|
52
|
+
|
53
|
+
# api: https://www.rubydoc.info/gems/gitlab/toplevel
|
54
|
+
# document: https://narkoz.github.io/gitlab/cli
|
55
|
+
|
56
|
+
Gitlab.configure do |config|
|
57
|
+
# set an API endpoint
|
58
|
+
# API endpoint URL, default: ENV['GITLAB_API_ENDPOINT']
|
59
|
+
config.endpoint = self.gb_config.gitlab.endpoint
|
60
|
+
|
61
|
+
# set a user private token
|
62
|
+
# user's private token or OAuth2 access token, default: ENV['GITLAB_API_PRIVATE_TOKEN']
|
63
|
+
config.private_token = self.gb_config.gitlab.private_token
|
64
|
+
|
65
|
+
# user agent
|
66
|
+
config.user_agent = "gb ruby gem[#{VERSION}"
|
67
|
+
end
|
68
|
+
|
69
|
+
user = nil
|
70
|
+
if !@assignee.nil?
|
71
|
+
user = gitlab_search_user(@assignee)
|
72
|
+
end
|
73
|
+
|
74
|
+
self.gb_config.projects.each_with_index do |project, index|
|
75
|
+
project_path = File.expand_path(project.name, './')
|
76
|
+
if File.exist?(project_path)
|
77
|
+
remote = 'origin'
|
78
|
+
info "Create branch '#{@working_branch}' for project '#{project.name}'"
|
79
|
+
g = Git.open(project_path)
|
80
|
+
else
|
81
|
+
g = Git.clone(project.git, project.name, :path => './')
|
82
|
+
end
|
83
|
+
|
84
|
+
gitlab_project = gitlab_search_project(project.name)
|
85
|
+
info "Find project #{gitlab_project.name} on #{gitlab_project.web_url}."
|
86
|
+
|
87
|
+
unless g.is_remote_branch?(@working_branch)
|
88
|
+
raise Error.new("Branch '#{@working_branch}' not exist in remote '#{remote}'.")
|
89
|
+
end
|
90
|
+
|
91
|
+
unless g.is_remote_branch?(@remote_branch)
|
92
|
+
raise Error.new("Branch '#{@remote_branch}' not exist in remote '#{remote}'.")
|
93
|
+
end
|
94
|
+
|
95
|
+
g.checkout(@working_branch)
|
96
|
+
# 更新本地代码
|
97
|
+
g.fetch(remote, :p => true, :t => true)
|
98
|
+
g.pull("origin", @working_branch)
|
99
|
+
g.pull("origin", @remote_branch)
|
100
|
+
# push到origin
|
101
|
+
g.push(remote, @working_branch)
|
102
|
+
|
103
|
+
compare_response = Gitlab.compare(gitlab_project.id, @remote_branch, @working_branch);
|
104
|
+
if compare_response.commits.size >= 1
|
105
|
+
if @show_diff
|
106
|
+
puts "\ncommits"
|
107
|
+
compare_response.commits.each_with_index do |commit, index|
|
108
|
+
unless index == 0
|
109
|
+
puts ""
|
110
|
+
end
|
111
|
+
puts " #{index} id:" + commit["id"]
|
112
|
+
puts " author:" + commit["author_name"]
|
113
|
+
puts " create at: " + commit["created_at"]
|
114
|
+
puts " title: " + commit["title"]
|
115
|
+
end
|
116
|
+
puts ""
|
117
|
+
end
|
118
|
+
else
|
119
|
+
info "Can't find new commit on #{@working_branch} to #{@remote_branch} in project #{project.name}."
|
120
|
+
puts
|
121
|
+
next
|
122
|
+
end
|
123
|
+
|
124
|
+
if compare_response.diffs.size >= 1
|
125
|
+
if @show_diff
|
126
|
+
puts "Diffs"
|
127
|
+
compare_response.diffs.each do |diff|
|
128
|
+
if diff["new_file"]
|
129
|
+
puts " created " + diff["new_path"]
|
130
|
+
elsif diff["renamed_file"]
|
131
|
+
puts " renamed " + diff["old_path"] + "=>" + diff["new_path"]
|
132
|
+
elsif diff["deleted_file"]
|
133
|
+
puts " deleted" + diff["old_path"]
|
134
|
+
else
|
135
|
+
puts " edited " + diff["new_path"]
|
136
|
+
end
|
137
|
+
|
138
|
+
diff = diff["diff"];
|
139
|
+
lines = diff.split("\n")
|
140
|
+
lines.each do |line|
|
141
|
+
puts " " + line
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
end
|
146
|
+
else
|
147
|
+
info "Can't find diff between #{@working_branch} and #{@remote_branch} in project #{project.name}."
|
148
|
+
puts
|
149
|
+
next
|
150
|
+
end
|
151
|
+
|
152
|
+
if user.nil?
|
153
|
+
users = gitlab_get_team_members(gitlab_project.id)
|
154
|
+
begin
|
155
|
+
info "\nSelect user name or index for review."
|
156
|
+
input_user = STDIN.gets.chomp
|
157
|
+
if input_user =~ /[[:digit:]]/
|
158
|
+
user = users[input_user.to_i - 1]
|
159
|
+
else
|
160
|
+
user = gitlab_search_user(input_user)
|
161
|
+
end
|
162
|
+
if user.nil?
|
163
|
+
error "Can not found user '#{input_user}'."
|
164
|
+
else
|
165
|
+
info "Assign to #{user.username}(#{user.name})"
|
166
|
+
end
|
167
|
+
end until !user.nil?
|
168
|
+
end
|
169
|
+
|
170
|
+
if @title.nil? || @title.empty?
|
171
|
+
begin
|
172
|
+
info "\nInput merge request title for project '#{project.name}'"
|
173
|
+
@title = STDIN.gets.chomp
|
174
|
+
end until @title.length > 0
|
175
|
+
end
|
176
|
+
|
177
|
+
# 总共 0 (差异 0),复用 0 (差异 0)
|
178
|
+
# remote:
|
179
|
+
# remote: To create a merge request for dev-v3.9.0-luobin, visit:
|
180
|
+
# remote: http://git.tianxiao100.com/tianxiao-ios/tianxiao/tianxiao-base-iphone-sdk/merge_requests/new?merge_request%5Bsource_branch%5D=dev-v3.9.0-luobin
|
181
|
+
# remote:
|
182
|
+
# To http://git.tianxiao100.com/tianxiao-ios/tianxiao/tianxiao-base-iphone-sdk.git
|
183
|
+
# * [new branch] dev-v3.9.0-luobin -> dev-v3.9.0-luobin
|
184
|
+
|
185
|
+
begin
|
186
|
+
merge_request = Gitlab.create_merge_request(gitlab_project.id, @title,
|
187
|
+
{ source_branch: @working_branch, target_branch: @remote_branch, assignee_id:user ? user.id : "" })
|
188
|
+
info "Create merge request for #{project.name} success. see detail url:#{merge_request.web_url}"
|
189
|
+
if !Gem.win_platform?
|
190
|
+
`open -a "/Applications/Google Chrome.app" '#{merge_request.web_url}/diffs'`
|
191
|
+
exitstatus = $?.exitstatus
|
192
|
+
if exitstatus != 0
|
193
|
+
raise Error.new("open chrome failed.")
|
194
|
+
else
|
195
|
+
# info "Please review diff, then input any to continue."
|
196
|
+
# STDIN.gets.chomp
|
197
|
+
end
|
198
|
+
end
|
199
|
+
rescue Gitlab::Error::Conflict => error
|
200
|
+
# merge exists
|
201
|
+
info "Merge request from '#{@working_branch}' to '#{@remote_branch}' exist."
|
202
|
+
rescue Gitlab::Error::Error => error
|
203
|
+
raise(error)
|
204
|
+
end
|
205
|
+
puts
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def gitlab_search_user(assignee)
|
211
|
+
users = Gitlab.user_search(assignee)
|
212
|
+
if users.size > 1
|
213
|
+
info "Find more than one user. you means which one?"
|
214
|
+
users.each do |user|
|
215
|
+
print user.name + ' '
|
216
|
+
end
|
217
|
+
info ""
|
218
|
+
raise Error.new("Find #{users.size} user named #{project.name}")
|
219
|
+
elsif users.size == 1
|
220
|
+
user = users[0]
|
221
|
+
else
|
222
|
+
raise Error.new("Can't find user #{assignee}.")
|
223
|
+
end
|
224
|
+
user
|
225
|
+
end
|
226
|
+
|
227
|
+
def gitlab_search_project(project_name)
|
228
|
+
projects = Gitlab.project_search(project_name)
|
229
|
+
if projects.size > 1
|
230
|
+
info "Find #{projects.size} project named #{project_name}. you means which one?"
|
231
|
+
projects.each do |project|
|
232
|
+
print project.name + ' '
|
233
|
+
end
|
234
|
+
print "\n"
|
235
|
+
raise Error.new("Find #{projects.size} project named #{project_name}")
|
236
|
+
|
237
|
+
elsif projects.size == 1
|
238
|
+
project = projects[0];
|
239
|
+
else
|
240
|
+
raise Error.new("Can't find project named '#{project_name}'.")
|
241
|
+
end
|
242
|
+
project
|
243
|
+
end
|
244
|
+
|
245
|
+
def gitlab_get_team_members(project_id)
|
246
|
+
users = Gitlab.project_usesrs(project_id).delete_if { |user|
|
247
|
+
user.username == 'root'
|
248
|
+
}
|
249
|
+
if users.size > 0
|
250
|
+
info "Find user to assign."
|
251
|
+
users.each_with_index do |user, index|
|
252
|
+
puts "#{index + 1}、#{user.username}(#{user.name})".green
|
253
|
+
end
|
254
|
+
else
|
255
|
+
raise Error.new("Can't find members in project '#{project_id}''.")
|
256
|
+
end
|
257
|
+
users
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'sub_command'
|
2
|
+
|
3
|
+
module Gb
|
4
|
+
class Start < SubCommand
|
5
|
+
|
6
|
+
self.summary = '创建对应工作分支,并同步到gitlab.'
|
7
|
+
|
8
|
+
self.description = <<-DESC
|
9
|
+
创建对应工作分支,并同步到gitlab.
|
10
|
+
DESC
|
11
|
+
|
12
|
+
self.arguments = [
|
13
|
+
CLAide::Argument.new('working_branch', true, false),
|
14
|
+
CLAide::Argument.new('remote_branch', true, false),
|
15
|
+
]
|
16
|
+
|
17
|
+
def self.options
|
18
|
+
[
|
19
|
+
["--force", "忽略工作分支是否存在,强制执行"],
|
20
|
+
].concat(super)
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(argv)
|
24
|
+
@working_branch = argv.shift_argument
|
25
|
+
@remote_branch = argv.shift_argument
|
26
|
+
@force = argv.flag?('force')
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate!
|
31
|
+
super
|
32
|
+
if @working_branch.nil?
|
33
|
+
help! 'working_branch is required.'
|
34
|
+
end
|
35
|
+
if @remote_branch.nil?
|
36
|
+
help! 'remote_branch is required.'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def run
|
41
|
+
remote = 'origin'
|
42
|
+
workspace_config = WorkSpaceConfig.new(@remote_branch, @working_branch)
|
43
|
+
|
44
|
+
self.gb_config.projects.each do |project|
|
45
|
+
project_path = File.expand_path(project.name, './')
|
46
|
+
if File.exist?(project_path)
|
47
|
+
g = Git.open(project_path)
|
48
|
+
else
|
49
|
+
g = Git.clone(project.git, project.name, :path => './')
|
50
|
+
end
|
51
|
+
|
52
|
+
if self.verbose?
|
53
|
+
# g.setLogger(Logger.new(STDOUT))
|
54
|
+
end
|
55
|
+
|
56
|
+
check_uncommit(g, project.name)
|
57
|
+
|
58
|
+
# 更新本地代码
|
59
|
+
g.fetch(remote, :p => true, :t => true)
|
60
|
+
# g.pull_opts(remote, g.current_branch, :p => true)
|
61
|
+
|
62
|
+
if !g.is_remote_branch?(@remote_branch)
|
63
|
+
raise Error.new("remote branch '#{@remote_branch}' does not exist for project '#{project.name}'.")
|
64
|
+
end
|
65
|
+
|
66
|
+
if g.is_remote_branch?(@working_branch) && !@force
|
67
|
+
raise Error.new("branch '#{@working_branch}' exist in remote '#{remote}' for project '#{project.name}'.")
|
68
|
+
end
|
69
|
+
|
70
|
+
if g.is_local_branch?(@working_branch) && !@force
|
71
|
+
raise Error.new("branch '#{@working_branch}' exist in local for project '#{project.name}'.")
|
72
|
+
end
|
73
|
+
|
74
|
+
# g.remote(remote).branch(@remote_branch).checkout()
|
75
|
+
# g.branch(@remote_branch).checkout
|
76
|
+
|
77
|
+
# git_cmd = "git remote set-branches #{remote} '#{@remote_branch}'"
|
78
|
+
# puts `#{git_cmd}`.chomp
|
79
|
+
#
|
80
|
+
# git_cmd = "git fetch --depth 1 #{remote} '#{@remote_branch}'"
|
81
|
+
# puts `#{git_cmd}`.chomp
|
82
|
+
#
|
83
|
+
# $ git remote set-branches origin 'remote_branch_name'
|
84
|
+
# $ git fetch --depth 1 origin remote_branch_name
|
85
|
+
# $ git checkout remote_branch_name
|
86
|
+
|
87
|
+
g.checkout(@remote_branch)
|
88
|
+
|
89
|
+
g.pull(remote, @remote_branch)
|
90
|
+
|
91
|
+
if g.is_local_branch?(@working_branch)
|
92
|
+
g.checkout(@working_branch)
|
93
|
+
g.pull(remote, @working_branch)
|
94
|
+
else
|
95
|
+
puts "create branch '#{@working_branch}' for project '#{project.name}'.".green
|
96
|
+
# 创建本地工作分支
|
97
|
+
g.checkout(@working_branch, :new_branch => true)
|
98
|
+
end
|
99
|
+
|
100
|
+
# 跟踪远程分支
|
101
|
+
g.track(remote, @remote_branch)
|
102
|
+
|
103
|
+
puts "push branch '#{@working_branch}' to remote for project '#{project.name}'.".green
|
104
|
+
# push到origin
|
105
|
+
g.push(remote, @working_branch)
|
106
|
+
|
107
|
+
puts
|
108
|
+
end
|
109
|
+
|
110
|
+
#保存新的workspace配置
|
111
|
+
self.save_workspace_config(workspace_config)
|
112
|
+
|
113
|
+
puts "create work branch '#{@working_branch}' from #{@remote_branch} and push to '#{remote}' success.".green
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'sub_command'
|
2
|
+
|
3
|
+
|
4
|
+
module Gb
|
5
|
+
|
6
|
+
class Status < SubCommand
|
7
|
+
|
8
|
+
self.summary = '查看工作分支代码状态'
|
9
|
+
|
10
|
+
self.description = <<-DESC
|
11
|
+
查看工作分支代码状态.
|
12
|
+
DESC
|
13
|
+
|
14
|
+
def run_in_workspace
|
15
|
+
|
16
|
+
self.gb_config.projects.each do |project|
|
17
|
+
project_path = File.expand_path(project.name, './')
|
18
|
+
|
19
|
+
if File.exist?(project_path)
|
20
|
+
info "for project '#{project.name}'..."
|
21
|
+
g = Git.open(project_path)
|
22
|
+
|
23
|
+
info "current branch '#{g.current_branch}'"
|
24
|
+
|
25
|
+
changed = g.status.changed
|
26
|
+
added = g.status.added
|
27
|
+
deleted = g.status.deleted
|
28
|
+
untracked = g.status.untracked
|
29
|
+
|
30
|
+
if !changed.empty?
|
31
|
+
alert = true
|
32
|
+
puts "modified files:".red
|
33
|
+
changed.each do |file, status|
|
34
|
+
puts (" M: " << file).red
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
if !added.empty?
|
39
|
+
alert = true
|
40
|
+
puts "added files:".red
|
41
|
+
added.each do |file, status|
|
42
|
+
puts (" A: " << file).red
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if !deleted.empty?
|
47
|
+
alert = true
|
48
|
+
puts "deleted files:".red
|
49
|
+
deleted.each do |file, status|
|
50
|
+
puts (" D: " << file).red
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if !untracked.empty?
|
55
|
+
alert = true
|
56
|
+
puts "untracked files:".red
|
57
|
+
untracked.each do |file, status|
|
58
|
+
puts (" " << file).red
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
if alert
|
63
|
+
|
64
|
+
else
|
65
|
+
info "git工作区无代码要提交"
|
66
|
+
end
|
67
|
+
puts
|
68
|
+
|
69
|
+
else
|
70
|
+
error "please run 'gb init first."
|
71
|
+
break
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|