gtlab 0.1.0

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.
@@ -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