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,104 @@
|
|
1
|
+
require 'sub_command'
|
2
|
+
|
3
|
+
|
4
|
+
module Gb
|
5
|
+
|
6
|
+
class CreateTag < SubCommand
|
7
|
+
|
8
|
+
self.summary = '新建tag'
|
9
|
+
|
10
|
+
self.description = <<-DESC
|
11
|
+
指定分支上新建tag.
|
12
|
+
DESC
|
13
|
+
|
14
|
+
self.arguments = [
|
15
|
+
CLAide::Argument.new('branch', true, false),
|
16
|
+
CLAide::Argument.new('tag_name', true, false),
|
17
|
+
]
|
18
|
+
|
19
|
+
def self.options
|
20
|
+
[
|
21
|
+
["--force", "忽略tag是否存在,强制执行"],
|
22
|
+
].concat(super)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(argv)
|
26
|
+
@branch = argv.shift_argument
|
27
|
+
@tag_name = argv.shift_argument
|
28
|
+
@force = argv.flag?('force')
|
29
|
+
super
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate!
|
33
|
+
super
|
34
|
+
if @branch.nil?
|
35
|
+
help! 'branch is required.'
|
36
|
+
end
|
37
|
+
if @tag_name.nil?
|
38
|
+
help! 'tag_name is required.'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def run
|
43
|
+
# api: https://www.rubydoc.info/gems/gitlab/toplevel
|
44
|
+
# document: https://narkoz.github.io/gitlab/cli
|
45
|
+
|
46
|
+
Gitlab.configure do |config|
|
47
|
+
# set an API endpoint
|
48
|
+
# API endpoint URL, default: ENV['GITLAB_API_ENDPOINT']
|
49
|
+
config.endpoint = self.gb_config.gitlab.endpoint
|
50
|
+
|
51
|
+
# set a user private token
|
52
|
+
# user's private token or OAuth2 access token, default: ENV['GITLAB_API_PRIVATE_TOKEN']
|
53
|
+
config.private_token = self.gb_config.gitlab.private_token
|
54
|
+
|
55
|
+
# user agent
|
56
|
+
config.user_agent = "gb ruby gem[#{VERSION}"
|
57
|
+
end
|
58
|
+
|
59
|
+
self.gb_config.projects.each do |project|
|
60
|
+
gitlab_project = gitlab_search_project(project.name)
|
61
|
+
info "find project #{gitlab_project.name} on #{gitlab_project.web_url}."
|
62
|
+
begin
|
63
|
+
tag = Gitlab.tag(gitlab_project.id, @tag_name)
|
64
|
+
rescue Gitlab::Error::NotFound => error
|
65
|
+
tag = nil
|
66
|
+
rescue Gitlab::Error::Error => error
|
67
|
+
raise(error)
|
68
|
+
end
|
69
|
+
|
70
|
+
if tag.nil?
|
71
|
+
Gitlab.create_tag(gitlab_project.id, @tag_name, @branch)
|
72
|
+
info "create tag '#{@tag_name}' success"
|
73
|
+
else
|
74
|
+
if @force
|
75
|
+
info "tag '#{@tag_name}' exist, skip."
|
76
|
+
else
|
77
|
+
help! "tag '#{@tag_name}' exist."
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
puts
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def gitlab_search_project(project_name)
|
86
|
+
projects = Gitlab.project_search(project_name)
|
87
|
+
if projects.size > 1
|
88
|
+
info "find #{projects.size} project named #{project_name}. you means which one?"
|
89
|
+
projects.each do |project|
|
90
|
+
print project.name + ' '
|
91
|
+
end
|
92
|
+
print "\n"
|
93
|
+
raise Error.new("find #{projects.size} project named #{project_name}")
|
94
|
+
|
95
|
+
elsif projects.size == 1
|
96
|
+
project = projects[0];
|
97
|
+
else
|
98
|
+
raise Error.new("can't find project named '#{project_name}'.")
|
99
|
+
end
|
100
|
+
project
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'sub_command'
|
2
|
+
|
3
|
+
|
4
|
+
module Gb
|
5
|
+
|
6
|
+
class DeleteTag < SubCommand
|
7
|
+
|
8
|
+
self.summary = '删除tag'
|
9
|
+
|
10
|
+
self.description = <<-DESC
|
11
|
+
删除指定tag.
|
12
|
+
DESC
|
13
|
+
|
14
|
+
self.arguments = [
|
15
|
+
CLAide::Argument.new('tag_name', true, false),
|
16
|
+
]
|
17
|
+
|
18
|
+
def self.options
|
19
|
+
[
|
20
|
+
["--force", "忽略tag是否存在,强制执行"],
|
21
|
+
].concat(super)
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(argv)
|
25
|
+
@tag_name = argv.shift_argument
|
26
|
+
@force = argv.flag?('force')
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate!
|
31
|
+
super
|
32
|
+
if @tag_name.nil?
|
33
|
+
help! 'tag_name is required.'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def run
|
38
|
+
# api: https://www.rubydoc.info/gems/gitlab/toplevel
|
39
|
+
# document: https://narkoz.github.io/gitlab/cli
|
40
|
+
|
41
|
+
Gitlab.configure do |config|
|
42
|
+
# set an API endpoint
|
43
|
+
# API endpoint URL, default: ENV['GITLAB_API_ENDPOINT']
|
44
|
+
config.endpoint = self.gb_config.gitlab.endpoint
|
45
|
+
|
46
|
+
# set a user private token
|
47
|
+
# user's private token or OAuth2 access token, default: ENV['GITLAB_API_PRIVATE_TOKEN']
|
48
|
+
config.private_token = self.gb_config.gitlab.private_token
|
49
|
+
|
50
|
+
# user agent
|
51
|
+
config.user_agent = "gb ruby gem[#{VERSION}"
|
52
|
+
end
|
53
|
+
|
54
|
+
info "ready delete tag '#{@tag_name}'.\n"
|
55
|
+
|
56
|
+
self.gb_config.projects.each do |project|
|
57
|
+
gitlab_project = gitlab_search_project(project.name)
|
58
|
+
info "find project #{gitlab_project.name} on #{gitlab_project.web_url}."
|
59
|
+
|
60
|
+
begin
|
61
|
+
tag = Gitlab.delete_tag(gitlab_project.id, @tag_name)
|
62
|
+
rescue Gitlab::Error::NotFound => error
|
63
|
+
tag = nil
|
64
|
+
if @force
|
65
|
+
raise(error)
|
66
|
+
else
|
67
|
+
info "tag '#{@tag_name}' not found, skip."
|
68
|
+
end
|
69
|
+
rescue Gitlab::Error::Error => error
|
70
|
+
raise(error)
|
71
|
+
end
|
72
|
+
if tag
|
73
|
+
info "delete tag '#{@tag_name}' success.\n"
|
74
|
+
end
|
75
|
+
puts
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def gitlab_search_project(project_name)
|
80
|
+
projects = Gitlab.project_search(project_name)
|
81
|
+
if projects.size > 1
|
82
|
+
info "find #{projects.size} project named #{project_name}. you means which one?"
|
83
|
+
projects.each do |project|
|
84
|
+
print project.name + ' '
|
85
|
+
end
|
86
|
+
print "\n"
|
87
|
+
raise Error.new("find #{projects.size} project named #{project_name}")
|
88
|
+
|
89
|
+
elsif projects.size == 1
|
90
|
+
project = projects[0];
|
91
|
+
else
|
92
|
+
raise Error.new("can't find project named '#{project_name}'.")
|
93
|
+
end
|
94
|
+
project
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'sub_command'
|
2
|
+
|
3
|
+
|
4
|
+
module Gb
|
5
|
+
|
6
|
+
class Forall < SubCommand
|
7
|
+
|
8
|
+
self.summary = '遍历所有工程执行命令'
|
9
|
+
self.description = <<-DESC
|
10
|
+
遍历所有工程执行命令.
|
11
|
+
gb forall -c="git reset --hard"
|
12
|
+
DESC
|
13
|
+
|
14
|
+
def self.options
|
15
|
+
[
|
16
|
+
["-c", "执行命令"],
|
17
|
+
].concat(super)
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(argv)
|
21
|
+
@command = argv.option('c')
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate!
|
26
|
+
super
|
27
|
+
if @command.nil?
|
28
|
+
help! 'command is required.'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_in_workspace
|
33
|
+
|
34
|
+
self.gb_config.projects.each do |project|
|
35
|
+
project_path = File.expand_path(project.name, './')
|
36
|
+
|
37
|
+
if File.exist?(project_path)
|
38
|
+
info "For project '#{project.name}'..."
|
39
|
+
g = Git.open(project_path)
|
40
|
+
Dir.chdir(project_path) do
|
41
|
+
result = `#{@command}`
|
42
|
+
puts result
|
43
|
+
end
|
44
|
+
puts
|
45
|
+
else
|
46
|
+
error "please run 'gb init first."
|
47
|
+
break
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'sub_command'
|
2
|
+
|
3
|
+
module Gb
|
4
|
+
|
5
|
+
class Init < SubCommand
|
6
|
+
|
7
|
+
self.summary = '根据yml配置,更新代码'
|
8
|
+
|
9
|
+
self.description = <<-DESC
|
10
|
+
根据yml配置,更新代码.
|
11
|
+
DESC
|
12
|
+
|
13
|
+
def run
|
14
|
+
mutex = Mutex.new
|
15
|
+
threads = []
|
16
|
+
self.gb_config.projects.each do |project|
|
17
|
+
t = Thread.new do
|
18
|
+
project_path = File.expand_path(project.name, './')
|
19
|
+
if File.exist?(project_path)
|
20
|
+
mutex.synchronize do
|
21
|
+
info project.name + ' exists, skip.'
|
22
|
+
end
|
23
|
+
else
|
24
|
+
Git.clone_without_env(project.git, project.name, :path => './')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
threads << t
|
28
|
+
end
|
29
|
+
threads.each do |t|
|
30
|
+
t.join
|
31
|
+
end
|
32
|
+
puts "#{self.gb_config.projects.size} projects init success.".green
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
require 'sub_command'
|
2
|
+
require 'ext/gitlab_ext'
|
3
|
+
|
4
|
+
module Gb
|
5
|
+
class Merge < SubCommand
|
6
|
+
|
7
|
+
self.summary = '创建gitlab merge request,注意跟review的区别是不会同步本地分支代码.'
|
8
|
+
|
9
|
+
self.description = <<-DESC
|
10
|
+
创建gitlab merge request,注意跟review的区别是不会同步本地分支代码.
|
11
|
+
|
12
|
+
gb merge from_branch to_branch
|
13
|
+
DESC
|
14
|
+
|
15
|
+
self.arguments = [
|
16
|
+
CLAide::Argument.new('from_branch', true, false),
|
17
|
+
CLAide::Argument.new('to_branch', true, false),
|
18
|
+
]
|
19
|
+
|
20
|
+
def self.options
|
21
|
+
[
|
22
|
+
["--assignee=[user name]", "指定review用户名称"],
|
23
|
+
["--title", "merge request标题"],
|
24
|
+
["--show-diff", "review前是否显示变更"],
|
25
|
+
].concat(super)
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(argv)
|
29
|
+
@from_branch = argv.shift_argument
|
30
|
+
@to_branch = argv.shift_argument
|
31
|
+
@assignee = argv.option('assignee')
|
32
|
+
@title = argv.option('title')
|
33
|
+
@show_diff = argv.flag?('show-diff')
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate!
|
38
|
+
super
|
39
|
+
if @from_branch.nil?
|
40
|
+
help! 'from_branch is required.'
|
41
|
+
end
|
42
|
+
if @to_branch.nil?
|
43
|
+
help! 'remote_branch is required.'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def run_in_workspace
|
48
|
+
remote = "origin"
|
49
|
+
|
50
|
+
# api: https://www.rubydoc.info/gems/gitlab/toplevel
|
51
|
+
# document: https://narkoz.github.io/gitlab/cli
|
52
|
+
|
53
|
+
Gitlab.configure do |config|
|
54
|
+
# set an API endpoint
|
55
|
+
# API endpoint URL, default: ENV['GITLAB_API_ENDPOINT']
|
56
|
+
config.endpoint = self.gb_config.gitlab.endpoint
|
57
|
+
|
58
|
+
# set a user private token
|
59
|
+
# user's private token or OAuth2 access token, default: ENV['GITLAB_API_PRIVATE_TOKEN']
|
60
|
+
config.private_token = self.gb_config.gitlab.private_token
|
61
|
+
|
62
|
+
# user agent
|
63
|
+
config.user_agent = "gb ruby gem[#{VERSION}"
|
64
|
+
end
|
65
|
+
|
66
|
+
user = nil
|
67
|
+
if !@assignee.nil?
|
68
|
+
user = gitlab_search_user(@assignee)
|
69
|
+
end
|
70
|
+
|
71
|
+
self.gb_config.projects.each_with_index do |project, index|
|
72
|
+
|
73
|
+
gitlab_project = gitlab_search_project(project.name)
|
74
|
+
info "Find project #{gitlab_project.name} on #{gitlab_project.web_url}."
|
75
|
+
|
76
|
+
begin
|
77
|
+
Gitlab.branch(gitlab_project.id, @from_branch)
|
78
|
+
rescue Gitlab::Error::NotFound => error
|
79
|
+
raise Error.new("Branch '#{@from_branch}' not exist in remote '#{remote}'.")
|
80
|
+
rescue Gitlab::Error::Error => error
|
81
|
+
raise(error)
|
82
|
+
end
|
83
|
+
|
84
|
+
begin
|
85
|
+
Gitlab.branch(gitlab_project.id, @to_branch)
|
86
|
+
rescue Gitlab::Error::NotFound => error
|
87
|
+
raise Error.new("Branch '#{@to_branch}' not exist in remote '#{remote}'.")
|
88
|
+
rescue Gitlab::Error::Error => error
|
89
|
+
raise(error)
|
90
|
+
end
|
91
|
+
|
92
|
+
compare_response = Gitlab.compare(gitlab_project.id, @to_branch, @from_branch);
|
93
|
+
if compare_response.commits.size >= 1
|
94
|
+
if @show_diff
|
95
|
+
puts "\ncommits"
|
96
|
+
compare_response.commits.each_with_index do |commit, index|
|
97
|
+
unless index == 0
|
98
|
+
puts ""
|
99
|
+
end
|
100
|
+
puts " #{index} id:" + commit["id"]
|
101
|
+
puts " author:" + commit["author_name"]
|
102
|
+
puts " create at: " + commit["created_at"]
|
103
|
+
puts " title: " + commit["title"]
|
104
|
+
end
|
105
|
+
puts ""
|
106
|
+
end
|
107
|
+
else
|
108
|
+
info "Can't find new commit on #{@from_branch} to #{@to_branch} in project #{project.name}."
|
109
|
+
puts
|
110
|
+
next
|
111
|
+
end
|
112
|
+
|
113
|
+
if compare_response.diffs.size >= 1
|
114
|
+
if @show_diff
|
115
|
+
puts "Diffs"
|
116
|
+
compare_response.diffs.each do |diff|
|
117
|
+
if diff["new_file"]
|
118
|
+
puts " created " + diff["new_path"]
|
119
|
+
elsif diff["renamed_file"]
|
120
|
+
puts " renamed " + diff["old_path"] + "=>" + diff["new_path"]
|
121
|
+
elsif diff["deleted_file"]
|
122
|
+
puts " deleted" + diff["old_path"]
|
123
|
+
else
|
124
|
+
puts " edited " + diff["new_path"]
|
125
|
+
end
|
126
|
+
|
127
|
+
diff = diff["diff"];
|
128
|
+
lines = diff.split("\n")
|
129
|
+
lines.each do |line|
|
130
|
+
puts " " + line
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
else
|
136
|
+
info "Can't find diff between #{@from_branch} and #{@to_branch} in project #{project.name}."
|
137
|
+
puts
|
138
|
+
next
|
139
|
+
end
|
140
|
+
|
141
|
+
if user.nil?
|
142
|
+
users = gitlab_get_team_members(gitlab_project.id)
|
143
|
+
begin
|
144
|
+
info "\nSelect user name or index for review."
|
145
|
+
input_user = STDIN.gets.chomp
|
146
|
+
if input_user =~ /[[:digit:]]/
|
147
|
+
user = users[input_user.to_i - 1]
|
148
|
+
else
|
149
|
+
user = gitlab_search_user(input_user)
|
150
|
+
end
|
151
|
+
if user.nil?
|
152
|
+
error "Can not found user '#{input_user}'."
|
153
|
+
else
|
154
|
+
info "Assign to #{user.username}(#{user.name})"
|
155
|
+
end
|
156
|
+
end until !user.nil?
|
157
|
+
end
|
158
|
+
|
159
|
+
if @title.nil? || @title.empty?
|
160
|
+
begin
|
161
|
+
info "\nInput merge request title for project '#{project.name}'"
|
162
|
+
@title = STDIN.gets.chomp
|
163
|
+
end until @title.length > 0
|
164
|
+
end
|
165
|
+
|
166
|
+
# 总共 0 (差异 0),复用 0 (差异 0)
|
167
|
+
# remote:
|
168
|
+
# remote: To create a merge request for dev-v3.9.0-luobin, visit:
|
169
|
+
# remote: http://git.tianxiao100.com/tianxiao-ios/tianxiao/tianxiao-base-iphone-sdk/merge_requests/new?merge_request%5Bfrom_branch%5D=dev-v3.9.0-luobin
|
170
|
+
# remote:
|
171
|
+
# To http://git.tianxiao100.com/tianxiao-ios/tianxiao/tianxiao-base-iphone-sdk.git
|
172
|
+
# * [new branch] dev-v3.9.0-luobin -> dev-v3.9.0-luobin
|
173
|
+
|
174
|
+
begin
|
175
|
+
merge_request = Gitlab.create_merge_request(gitlab_project.id, @title,
|
176
|
+
{ source_branch: @from_branch, target_branch: @to_branch, assignee_id:user ? user.id : "" })
|
177
|
+
info "Create merge request for #{project.name} success. see detail url:#{merge_request.web_url}"
|
178
|
+
if !Gem.win_platform?
|
179
|
+
`open -a "/Applications/Google Chrome.app" '#{merge_request.web_url}/diffs'`
|
180
|
+
exitstatus = $?.exitstatus
|
181
|
+
if exitstatus != 0
|
182
|
+
raise Error.new("open chrome failed.")
|
183
|
+
else
|
184
|
+
# info "Please review diff, then input any to continue."
|
185
|
+
# STDIN.gets.chomp
|
186
|
+
end
|
187
|
+
end
|
188
|
+
rescue Gitlab::Error::Conflict => error
|
189
|
+
# merge exists
|
190
|
+
info "Merge request from '#{@from_branch}' to '#{@to_branch}' exist."
|
191
|
+
rescue Gitlab::Error::Error => error
|
192
|
+
raise(error)
|
193
|
+
end
|
194
|
+
puts
|
195
|
+
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def gitlab_search_user(assignee)
|
200
|
+
users = Gitlab.user_search(assignee)
|
201
|
+
if users.size > 1
|
202
|
+
info "Find more than one user. you means which one?"
|
203
|
+
users.each do |user|
|
204
|
+
print user.name + ' '
|
205
|
+
end
|
206
|
+
info ""
|
207
|
+
raise Error.new("Find #{users.size} user named #{project.name}")
|
208
|
+
elsif users.size == 1
|
209
|
+
user = users[0]
|
210
|
+
else
|
211
|
+
raise Error.new("Can't find user #{assignee}.")
|
212
|
+
end
|
213
|
+
user
|
214
|
+
end
|
215
|
+
|
216
|
+
def gitlab_search_project(project_name)
|
217
|
+
projects = Gitlab.project_search(project_name)
|
218
|
+
if projects.size > 1
|
219
|
+
info "Find #{projects.size} project named #{project_name}. you means which one?"
|
220
|
+
projects.each do |project|
|
221
|
+
print project.name + ' '
|
222
|
+
end
|
223
|
+
print "\n"
|
224
|
+
raise Error.new("Find #{projects.size} project named #{project_name}")
|
225
|
+
|
226
|
+
elsif projects.size == 1
|
227
|
+
project = projects[0];
|
228
|
+
else
|
229
|
+
raise Error.new("Can't find project named '#{project_name}'.")
|
230
|
+
end
|
231
|
+
project
|
232
|
+
end
|
233
|
+
|
234
|
+
def gitlab_get_team_members(project_id)
|
235
|
+
users = Gitlab.project_usesrs(project_id).delete_if { |user|
|
236
|
+
user.username == 'root'
|
237
|
+
}
|
238
|
+
if users.size > 0
|
239
|
+
info "Find user to assign."
|
240
|
+
users.each_with_index do |user, index|
|
241
|
+
puts "#{index + 1}、#{user.username}(#{user.name})".green
|
242
|
+
end
|
243
|
+
else
|
244
|
+
raise Error.new("Can't find members in project '#{project_id}''.")
|
245
|
+
end
|
246
|
+
users
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|