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,45 @@
|
|
1
|
+
require 'sub_command'
|
2
|
+
|
3
|
+
|
4
|
+
module Gb
|
5
|
+
|
6
|
+
class Sync < SubCommand
|
7
|
+
|
8
|
+
self.summary = '更新工作分支代码'
|
9
|
+
|
10
|
+
self.description = <<-DESC
|
11
|
+
根据yml配置,更新代码.
|
12
|
+
DESC
|
13
|
+
|
14
|
+
def run_in_workspace
|
15
|
+
|
16
|
+
remote = 'origin'
|
17
|
+
workspace_config = self.workspace_config
|
18
|
+
|
19
|
+
info "current work branch '#{workspace_config.workspace_branch}', remote branch '#{workspace_config.remote_branch}'."
|
20
|
+
|
21
|
+
self.gb_config.projects.each do |project|
|
22
|
+
project_path = File.expand_path(project.name, './')
|
23
|
+
|
24
|
+
if File.exist?(project_path)
|
25
|
+
info "sync project '#{project.name}'..."
|
26
|
+
g = Git.open(project_path)
|
27
|
+
if workspace_config.workspace_branch != g.current_branch
|
28
|
+
error "current branch is not work branch(#{workspace_config.workspace_branch})."
|
29
|
+
exit(1)
|
30
|
+
end
|
31
|
+
g.fetch(remote, :p => true, :t => true)
|
32
|
+
g.pull("origin", workspace_config.workspace_branch)
|
33
|
+
g.pull("origin", workspace_config.remote_branch)
|
34
|
+
puts
|
35
|
+
|
36
|
+
else
|
37
|
+
error "please run 'gb init first."
|
38
|
+
break
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'sub_command'
|
2
|
+
|
3
|
+
|
4
|
+
module Gb
|
5
|
+
|
6
|
+
class Workspace < SubCommand
|
7
|
+
|
8
|
+
self.summary = '查看当前工作区信息'
|
9
|
+
|
10
|
+
self.description = <<-DESC
|
11
|
+
查看当前工作区信息.
|
12
|
+
DESC
|
13
|
+
|
14
|
+
def run_in_workspace
|
15
|
+
|
16
|
+
remote = 'origin'
|
17
|
+
workspace_config = self.workspace_config
|
18
|
+
|
19
|
+
info "current work branch '#{workspace_config.workspace_branch}'"
|
20
|
+
info "track remote branch '#{workspace_config.remote_branch}'."
|
21
|
+
puts
|
22
|
+
|
23
|
+
self.gb_config.projects.each do |project|
|
24
|
+
project_path = File.expand_path(project.name, './')
|
25
|
+
|
26
|
+
if File.exist?(project_path)
|
27
|
+
info "Project '#{project.name}'..."
|
28
|
+
g = Git.open(project_path)
|
29
|
+
info "current branch '#{g.current_branch}'."
|
30
|
+
else
|
31
|
+
error "please run 'gb init first."
|
32
|
+
break
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Gb
|
2
|
+
class GbConfig
|
3
|
+
|
4
|
+
attr_reader :projects
|
5
|
+
attr_reader :gitlab
|
6
|
+
attr_reader :config_path
|
7
|
+
|
8
|
+
def initialize(config_path, node)
|
9
|
+
@config_path = config_path
|
10
|
+
|
11
|
+
gitlab = node['gitlab']
|
12
|
+
@gitlab = GitlabConfig.new(gitlab)
|
13
|
+
|
14
|
+
@projects = []
|
15
|
+
projects = node['projects']
|
16
|
+
if !projects.nil?
|
17
|
+
projects.each do |project|
|
18
|
+
projectConfig = ProjectConfig.new(project)
|
19
|
+
@projects << projectConfig
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.load_file(config_path)
|
25
|
+
node = YAML.load_file(config_path)
|
26
|
+
GbConfig.new(config_path, node)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.load_yml(yml)
|
30
|
+
node = YAML.load(yml)
|
31
|
+
GbConfig.new(nil, node)
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_dictionary
|
35
|
+
projects = self.projects.map do |project|
|
36
|
+
project.to_dictionary
|
37
|
+
end
|
38
|
+
{"projects"=>projects, "gitlab"=>self.gitlab.to_dictionary}
|
39
|
+
end
|
40
|
+
|
41
|
+
class ProjectConfig
|
42
|
+
attr_reader :name
|
43
|
+
attr_reader :git
|
44
|
+
|
45
|
+
def initialize(node)
|
46
|
+
@name = node['name']
|
47
|
+
@git = node['git']
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_dictionary
|
51
|
+
{"name"=>self.name, "git"=>self.git}
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
class GitlabConfig
|
57
|
+
attr_reader :endpoint
|
58
|
+
attr_accessor :private_token
|
59
|
+
|
60
|
+
def initialize(node)
|
61
|
+
@endpoint = node['endpoint']
|
62
|
+
@private_token = node['private_token']
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_dictionary
|
66
|
+
{"endpoint"=>self.endpoint, "private_token"=>private_token}
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
module Gb
|
3
|
+
class WorkSpaceConfig
|
4
|
+
attr_reader :remote_branch
|
5
|
+
attr_reader :workspace_branch
|
6
|
+
|
7
|
+
def initialize(remote_branch, workspace_branch)
|
8
|
+
@remote_branch = remote_branch
|
9
|
+
@workspace_branch = workspace_branch
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.load_file(yaml_filename)
|
13
|
+
node = YAML.load_file(yaml_filename)
|
14
|
+
remote_branch = node['remote_branch']
|
15
|
+
workspace_branch = node['workspace_branch']
|
16
|
+
return WorkSpaceConfig.new(remote_branch, workspace_branch)
|
17
|
+
end
|
18
|
+
|
19
|
+
def save(path)
|
20
|
+
File.open(path, 'w') do |file|
|
21
|
+
Psych.dump({'remote_branch' => @remote_branch, 'workspace_branch' => @workspace_branch}, file)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/ext/git_ext.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'git'
|
2
|
+
|
3
|
+
|
4
|
+
module Git
|
5
|
+
def self.clone_without_env(repository, name, opts = {})
|
6
|
+
opts = Git::Lib.new.clone_without_env(repository, name, opts)
|
7
|
+
Base.new(opts)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Patches
|
12
|
+
module Git
|
13
|
+
|
14
|
+
module Base
|
15
|
+
|
16
|
+
def track(remote, branch)
|
17
|
+
self.lib.track(remote, branch)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
module Lib
|
22
|
+
def initialize(*args)
|
23
|
+
super
|
24
|
+
# @logger = Logger.new(STDOUT)
|
25
|
+
end
|
26
|
+
|
27
|
+
def run_command(git_cmd, &block)
|
28
|
+
git_cmd = git_cmd.gsub(/2>&1$/, '')
|
29
|
+
return IO.popen(git_cmd, &block) if block_given?
|
30
|
+
|
31
|
+
`#{git_cmd}`.chomp
|
32
|
+
end
|
33
|
+
|
34
|
+
def track(remote, branch)
|
35
|
+
arr_opts = []
|
36
|
+
arr_opts << '-u'
|
37
|
+
arr_opts << "#{remote}/#{branch}"
|
38
|
+
command('branch', arr_opts)
|
39
|
+
end
|
40
|
+
|
41
|
+
def clone_without_env(repository, name, opts = {})
|
42
|
+
@path = opts[:path] || '.'
|
43
|
+
clone_dir = opts[:path] ? File.join(@path, name) : name
|
44
|
+
|
45
|
+
arr_opts = []
|
46
|
+
arr_opts << '--bare' if opts[:bare]
|
47
|
+
arr_opts << '--branch' << opts[:branch] if opts[:branch]
|
48
|
+
arr_opts << '--depth' << opts[:depth].to_i if opts[:depth] && opts[:depth].to_i > 0
|
49
|
+
arr_opts << '--config' << opts[:gitl_config] if opts[:gitl_config]
|
50
|
+
arr_opts << '--origin' << opts[:remote] || opts[:origin] if opts[:remote] || opts[:origin]
|
51
|
+
arr_opts << '--recursive' if opts[:recursive]
|
52
|
+
arr_opts << "--mirror" if opts[:mirror]
|
53
|
+
|
54
|
+
arr_opts << '--'
|
55
|
+
|
56
|
+
arr_opts << repository
|
57
|
+
arr_opts << clone_dir
|
58
|
+
|
59
|
+
command_without_env('clone', arr_opts)
|
60
|
+
|
61
|
+
(opts[:bare] or opts[:mirror]) ? {:repository => clone_dir} : {:working_directory => clone_dir}
|
62
|
+
end
|
63
|
+
|
64
|
+
def command_without_env(cmd, opts = [], chdir = true, redirect = '', &block)
|
65
|
+
global_opts = []
|
66
|
+
global_opts << "--git-dir=#{@git_dir}" if !@git_dir.nil?
|
67
|
+
global_opts << "--work-tree=#{@git_work_dir}" if !@git_work_dir.nil?
|
68
|
+
|
69
|
+
opts = [opts].flatten.map {|s| escape(s) }.join(' ')
|
70
|
+
|
71
|
+
global_opts = global_opts.flatten.map {|s| escape(s) }.join(' ')
|
72
|
+
|
73
|
+
git_cmd = "#{::Git::Base.config.binary_path} #{global_opts} #{cmd} #{opts} #{redirect} 2>&1"
|
74
|
+
|
75
|
+
output = nil
|
76
|
+
|
77
|
+
command_thread = nil;
|
78
|
+
|
79
|
+
exitstatus = nil
|
80
|
+
|
81
|
+
command_thread = Thread.new do
|
82
|
+
output = run_command(git_cmd, &block)
|
83
|
+
exitstatus = $?.exitstatus
|
84
|
+
end
|
85
|
+
command_thread.join
|
86
|
+
|
87
|
+
if @logger
|
88
|
+
@logger.info(git_cmd)
|
89
|
+
@logger.debug(output)
|
90
|
+
end
|
91
|
+
|
92
|
+
if exitstatus > 1 || (exitstatus == 1 && output != '')
|
93
|
+
raise Git::GitExecuteError.new(git_cmd + ':' + output.to_s)
|
94
|
+
end
|
95
|
+
|
96
|
+
return output
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
module Status
|
102
|
+
def fetch_untracked
|
103
|
+
ignore = @base.lib.ignored_files
|
104
|
+
|
105
|
+
Dir.chdir(@base.dir.path) do
|
106
|
+
Dir.glob('**/*', File::FNM_DOTMATCH) do |file|
|
107
|
+
next if @files[file] || File.directory?(file) ||
|
108
|
+
ignore.include?(file) || file =~ %r{^.git\/.+} || file =~ %r{^(.*\/)?.gitkeep$}
|
109
|
+
|
110
|
+
@files[file] = { path: file, untracked: true }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
Git::Status.prepend(Patches::Git::Status)
|
119
|
+
Git::Lib.prepend(Patches::Git::Lib)
|
120
|
+
Git::Base.prepend(Patches::Git::Base)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Patches
|
4
|
+
# Defines methods related to projects.
|
5
|
+
# @see https://docs.gitlab.com/ce/api/projects.html
|
6
|
+
module Projects
|
7
|
+
|
8
|
+
# Gets a list of project users.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# Gitlab.project_usesrs(42)
|
12
|
+
# Gitlab.project_usesrs('gitlab')
|
13
|
+
#
|
14
|
+
# @param [Integer, String] project The ID or path of a project.
|
15
|
+
# @param [Hash] options A customizable set of options.
|
16
|
+
# @option options [Integer] :page The page number.
|
17
|
+
# @option options [Integer] :per_page The number of results per page.
|
18
|
+
# @return [Array<Gitlab::ObjectifiedHash>]
|
19
|
+
def project_usesrs(project, options = {})
|
20
|
+
get("/projects/#{url_encode project}/users", query: options)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
Gitlab::Client.prepend(Patches::Projects)
|
data/lib/gb.rb
ADDED
data/lib/gb/version.rb
ADDED
data/lib/sub_command.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'command'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'config/gb_config'
|
4
|
+
require 'colored2'
|
5
|
+
require 'gitlab'
|
6
|
+
require 'ext/git_ext'
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
module Gb
|
10
|
+
class SubCommand < Command
|
11
|
+
|
12
|
+
self.ignore_in_command_lookup = true
|
13
|
+
attr_reader :gb_config
|
14
|
+
|
15
|
+
def self.options
|
16
|
+
[
|
17
|
+
['--config=[Gb.yml]', 'gb配置, 默认为Gb.yml'],
|
18
|
+
].concat(super)
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(argv)
|
22
|
+
@yml = argv.option('config')
|
23
|
+
if @yml.nil?
|
24
|
+
@yml = 'Gb.yml'
|
25
|
+
end
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate!
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
def run
|
34
|
+
workspace_path = "./"
|
35
|
+
find_workspace = false;
|
36
|
+
begin
|
37
|
+
result = nil
|
38
|
+
Dir.chdir(workspace_path) do
|
39
|
+
result = Dir.glob('.gb', File::FNM_DOTMATCH)
|
40
|
+
end
|
41
|
+
if result.length > 0
|
42
|
+
find_workspace = true
|
43
|
+
break
|
44
|
+
else
|
45
|
+
workspace_path = File.expand_path("../", workspace_path)
|
46
|
+
end
|
47
|
+
end while workspace_path.length > 0 && workspace_path != "/"
|
48
|
+
|
49
|
+
if find_workspace
|
50
|
+
Dir.chdir(workspace_path) do
|
51
|
+
self.run_in_workspace()
|
52
|
+
end
|
53
|
+
else
|
54
|
+
raise Error.new("Current path is not gb workspace. please run 'gb start' first.")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def run_in_workspace
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def gb_config
|
63
|
+
if File.exist?(@yml)
|
64
|
+
@gb_config = GbConfig.load_file(@yml)
|
65
|
+
else
|
66
|
+
help! "gb config not found. please run 'gb create' first."
|
67
|
+
end
|
68
|
+
@gb_config
|
69
|
+
end
|
70
|
+
|
71
|
+
def workspace_config
|
72
|
+
if @workspace_config.nil?
|
73
|
+
filename = '.gb'
|
74
|
+
# workspace_config_path = File.expand_path(filename, File.dirname(self.gb_config.config_path))
|
75
|
+
workspace_config_path = filename
|
76
|
+
if !File.exist?(workspace_config_path)
|
77
|
+
help! "workspace config not found. please run 'gb start' first."
|
78
|
+
end
|
79
|
+
@workspace_config = WorkSpaceConfig.load_file(workspace_config_path)
|
80
|
+
end
|
81
|
+
@workspace_config
|
82
|
+
end
|
83
|
+
|
84
|
+
def save_workspace_config(workspace_config)
|
85
|
+
filename = '.gb'
|
86
|
+
# workspace_config_path = File.expand_path(filename, File.dirname(self.gb_config.config_path))
|
87
|
+
workspace_config_path = filename
|
88
|
+
workspace_config.save(workspace_config_path)
|
89
|
+
@workspace_config = workspace_config
|
90
|
+
end
|
91
|
+
|
92
|
+
def check_uncommit(g, project_name)
|
93
|
+
changed = g.status.changed
|
94
|
+
added = g.status.added
|
95
|
+
deleted = g.status.deleted
|
96
|
+
untracked = g.status.untracked
|
97
|
+
|
98
|
+
if !changed.empty?
|
99
|
+
alert = true
|
100
|
+
puts "modified files:".red
|
101
|
+
changed.each do |file, status|
|
102
|
+
puts (" M: " << file).red
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
if !added.empty?
|
107
|
+
alert = true
|
108
|
+
puts "added files:".red
|
109
|
+
added.each do |file, status|
|
110
|
+
puts (" A: " << file).red
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
if !deleted.empty?
|
115
|
+
alert = true
|
116
|
+
puts "deleted files:".red
|
117
|
+
deleted.each do |file, status|
|
118
|
+
puts (" D: " << file).red
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
if !untracked.empty?
|
123
|
+
alert = true
|
124
|
+
puts "untracked files:".red
|
125
|
+
untracked.each do |file, status|
|
126
|
+
puts (" " << file).red
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
if alert
|
131
|
+
puts "exist uncommit files in current branch '#{g.current_branch}' for '#{project_name}'. ignore it? y/n "
|
132
|
+
flag = STDIN.gets.chomp
|
133
|
+
unless flag.downcase == "y"
|
134
|
+
exit
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|