gb 0.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 +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
|