gtlab 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,116 @@
1
+ require 'sub_command'
2
+
3
+ module Gitl
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.gitl_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,2 @@
1
+ class Status
2
+ end
@@ -0,0 +1,45 @@
1
+ require 'sub_command'
2
+
3
+
4
+ module Gitl
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.gitl_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 'gitl init' first."
38
+ break
39
+ end
40
+
41
+ end
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,71 @@
1
+ module Gitl
2
+ class GitlConfig
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
+ projects.each do |project|
17
+ projectConfig = ProjectConfig.new(project)
18
+ @projects << projectConfig
19
+ end
20
+ end
21
+
22
+ def self.load_file(config_path)
23
+ node = YAML.load_file(config_path)
24
+ GitlConfig.new(config_path, node)
25
+ end
26
+
27
+ def self.load_yml(yml)
28
+ node = YAML.load(yml)
29
+ GitlConfig.new(nil, node)
30
+ end
31
+
32
+ def to_dictionary
33
+ projects = self.projects.map do |project|
34
+ project.to_dictionary
35
+ end
36
+ {"projects"=>projects, "gitlab"=>self.gitlab.to_dictionary}
37
+ end
38
+
39
+ class ProjectConfig
40
+ attr_reader :name
41
+ attr_reader :git
42
+
43
+ def initialize(node)
44
+ @name = node['name']
45
+ @git = node['git']
46
+ end
47
+
48
+ def to_dictionary
49
+ {"name"=>self.name, "git"=>self.git}
50
+ end
51
+
52
+ end
53
+
54
+ class GitlabConfig
55
+ attr_reader :endpoint
56
+ attr_accessor :private_token
57
+
58
+ def initialize(node)
59
+ @endpoint = node['endpoint']
60
+ @private_token = node['private_token']
61
+ end
62
+
63
+ def to_dictionary
64
+ {"endpoint"=>self.endpoint, "private_token"=>private_token}
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+ end
71
+
@@ -0,0 +1,25 @@
1
+
2
+ module Gitl
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
@@ -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,8 @@
1
+ require 'gitl/version'
2
+
3
+
4
+ module Gitl
5
+
6
+
7
+
8
+ end
@@ -0,0 +1,3 @@
1
+ module Gitl
2
+ VERSION = "0.1.0"
3
+ end
@@ -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)
@@ -0,0 +1,139 @@
1
+ require 'command'
2
+ require 'rubygems'
3
+ require 'config/gitl_config'
4
+ require 'colored2'
5
+ require 'gitlab'
6
+ require 'git_ext'
7
+ require 'yaml'
8
+
9
+ module Gitl
10
+ class SubCommand < Command
11
+
12
+ self.ignore_in_command_lookup = true
13
+ attr_reader :gitl_config
14
+
15
+ def self.options
16
+ [
17
+ ['--config=[Gitl.yml]', 'gitl配置, 默认为Gitl.yml'],
18
+ ].concat(super)
19
+ end
20
+
21
+ def initialize(argv)
22
+ @yml = argv.option('config')
23
+ if @yml.nil?
24
+ @yml = 'Gitl.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('.gitl', 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 gitl workspace.")
55
+ end
56
+ end
57
+
58
+ def run_in_workspace
59
+
60
+ end
61
+
62
+ def gitl_config
63
+ if File.exist?(@yml)
64
+ @gitl_config = GitlConfig.load_file(@yml)
65
+ else
66
+ help! 'config is required.'
67
+ end
68
+ @gitl_config
69
+ end
70
+
71
+ def workspace_config
72
+ if @workspace_config.nil?
73
+ filename = '.gitl'
74
+ # workspace_config_path = File.expand_path(filename, File.dirname(self.gitl_config.config_path))
75
+ workspace_config_path = filename
76
+ if !File.exist?(workspace_config_path)
77
+ help! "workspace config not found. please run 'gitl 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 = '.gitl'
86
+ # workspace_config_path = File.expand_path(filename, File.dirname(self.gitl_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