coaster 1.3.32 → 1.3.34

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ade22d0084d98fb4169986fce083c991fdf00b2d5e95a4fec2f2fb2a03b0ffbb
4
- data.tar.gz: 43bedfaf5b382b1817bac45efd7ed1416f75696dc625faecf7be4f680e532985
3
+ metadata.gz: 7b74632abeea191ac11160939d23c3bd946cb48880bcf0d804513d4a1571cacd
4
+ data.tar.gz: cac164004edff9cd0454714e60cf4f896a2d96f61fa51ac16e1b3f1cd1ac0cfd
5
5
  SHA512:
6
- metadata.gz: 0bc77179288fb0841bf49646c799f75793f03e941032ee04fca006113a516f14f984cf07222a56e4e93fd49f45b3469726e84428bca177b86341d4cf3dbe1928
7
- data.tar.gz: 144d370265cfc0ab7618edd71f66239269c039710a3192579d53badefa67c54e49db75646bfa437af91167a73243f0a6bbb25039c46b9be504110797a86a8329
6
+ metadata.gz: 5ec894acd59493e72c0e644461ff9b6d607dff5b6b13131fe28141e7a48922a703488345ba326d6bc5b2a86729c1f1b35117fd553e5d887fb3e1698016fb8e37
7
+ data.tar.gz: 6cd29ad2fd087537de3b366bd11d8da6f0aee6ac7c81f1d42f71324ebbd6c729f8989c9fdf7ee3debce018a7fef2880309116a48ec7ae560b5d726e87f1d1427
data/bin/coaster CHANGED
@@ -5,7 +5,7 @@ require 'coaster/git'
5
5
 
6
6
  command = ARGV.shift
7
7
  if command == 'git'
8
- repo = Git::Repository.new(Dir.pwd)
8
+ repo = Coaster::Git::Repository.new(Dir.pwd)
9
9
  subcommand = ARGV.shift
10
10
  repo.send(subcommand, *ARGV)
11
11
  else
@@ -0,0 +1,161 @@
1
+ module Coaster
2
+ class CmdOptions
3
+ class << self
4
+ def options_to_s(options)
5
+ case options
6
+ when Hash then options_h_to_s(options)
7
+ when Array, Set then options.map{|o| options_to_s(o)}.join(' ')
8
+ else options
9
+ end
10
+ end
11
+
12
+ def options_s_to_h(options)
13
+ opts = {}
14
+ options = " #{options}"
15
+ options_indexes = options.enum_for(:scan, / -\w| --\w+| -- /).map { Regexp.last_match.begin(0) }
16
+ options_indexes << 0
17
+ options_indexes.each_cons(2) do |a, b|
18
+ option = options[a+1..b-1]
19
+ h = option_s_to_h(option)
20
+ options_h_merger(h, base: opts)
21
+ end
22
+ opts
23
+ end
24
+
25
+ def option_s_to_h(option)
26
+ if option.start_with?(/--\w/)
27
+ opt = option.split('=', 2)
28
+ opt << '' if opt.length == 1
29
+ elsif option.start_with?(/-\w/)
30
+ opt = option.split(' ', 2)
31
+ opt << '' if opt.length == 1
32
+ elsif option.start_with?('-- ')
33
+ opt = ['--', option[3..-1].split(' ')]
34
+ else
35
+ return {}
36
+ end
37
+ opt[1] = opt[1].split(',') if opt[1].include?(',')
38
+ opt[1] = opt[1].map{|s| s.include?('=') ? Hash[s.split('=', 2)] : s}.to_h if opt[1].is_a?(Array)
39
+ [opt].to_h
40
+ end
41
+
42
+ def option_v_to_s(option_v)
43
+ case option_v
44
+ when Hash then option_v.map{|vk,vv| Set[vk, vv]}.to_set
45
+ when Array then option_v.map{|v| option_v_to_s(v)}.join(',')
46
+ when Set then option_v.map{|v| option_v_to_s(v)}.join('=')
47
+ else
48
+ option_v = (option_v || '').to_s
49
+ option_v = option_v.gsub(/"/, '\"')
50
+ option_v = "\"#{option_v}\"" if option_v.include?(' ')
51
+ option_v
52
+ end
53
+ end
54
+
55
+ def options_h_to_s(options)
56
+ opts = []
57
+
58
+ # multiple options can be passed by set
59
+ options.map do |k, v|
60
+ if v.is_a?(Set)
61
+ v.each {|set_v| opts << [k, set_v]}
62
+ else
63
+ opts << [k, v]
64
+ end
65
+ end
66
+
67
+ targets = ''
68
+ parsed = opts.map do |k, v|
69
+ if k.start_with?(/--\w/)
70
+ v = option_v_to_s(v)
71
+ if v.is_a?(Set)
72
+ v.map {|vv| "#{k}=#{option_v_to_s(vv)}" }.join(' ')
73
+ else
74
+ "#{k}#{v.length > 0 ? "=#{v}" : ''}" # ex, --config-env=<name>=<envvar>
75
+ end
76
+ elsif k.start_with?(/-\w/)
77
+ v = option_v_to_s(v)
78
+ if v.is_a?(Set)
79
+ v.map {|vv| "#{k} #{option_v_to_s(vv)}"}.join(' ')
80
+ else
81
+ "#{k} #{v}" # ex, -c <name>=<value>
82
+ end
83
+ elsif k == '--'
84
+ if v.present?
85
+ v = Array.wrap(v)
86
+ v = v.map{|e| option_v_to_s(e)}.join(' ')
87
+ targets = "-- #{v}" # ex, -- <args>
88
+ ''
89
+ end
90
+ else
91
+ raise "Unknown option: #{k}"
92
+ end
93
+ end
94
+
95
+ parsed << targets
96
+ parsed.join(' ')
97
+ end
98
+ end
99
+
100
+ attr_reader :cmd, :sub_cmd, :args, :remain_args, :options, :str
101
+
102
+ def initialize(cmd, *args, **options)
103
+ arg_options = args.extract_options!
104
+ options = options.merge(arg_options)
105
+ @cmd = cmd
106
+ @cmd, @sub_cmd = @cmd if @cmd.is_a?(Array)
107
+ remain_ix = args.index{ |k| k == '--'}
108
+ if remain_ix
109
+ @remain_args = args[remain_ix+1..-1]
110
+ @args = args[0...remain_ix]
111
+ else
112
+ @args = args
113
+ end
114
+ @args.delete_if do |arg|
115
+ if arg.is_a?(self.class)
116
+ options = arg.to_h.merge(options)
117
+ true
118
+ else
119
+ false
120
+ end
121
+ end
122
+ options['--'] ||= []
123
+ options['--'] += @remain_args if @remain_args
124
+ @options = options
125
+ @args << self.class.options_to_s(options).strip
126
+ @str = @args.join(' ')
127
+ end
128
+
129
+ def parser_proc(*args)
130
+ raise 'Not implemented'
131
+ end
132
+
133
+ def parser
134
+ parser_proc = parser_proc(@cmd, @sub_cmd)
135
+ instance_exec(&parser_proc)
136
+ end
137
+
138
+ def to_h
139
+ return @hash if defined?(@hash)
140
+ @hash = {}
141
+ remain_args = parser.parse!(@str.split(' '))
142
+ @hash['--'] = remain_args if remain_args.any?
143
+ @hash
144
+ end
145
+ delegate :[], :[]=, :key?, :map, :each, to: :to_h
146
+
147
+ def merge(*args, **options)
148
+ if args.first.is_a?(CmdOptions)
149
+ other = args.shift
150
+ else
151
+ other = self.class.new([@cmd, @sub_cmd], *args, **options)
152
+ end
153
+ self.class.new(to_h.merge(other.to_h))
154
+ end
155
+
156
+ def to_s
157
+ return self.class.options_h_to_s(@hash) if defined?(@hash)
158
+ @str
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,118 @@
1
+ require 'coaster/cmd_options'
2
+
3
+ module Coaster
4
+ module Git
5
+ class Options < ::Coaster::CmdOptions
6
+ OPTION_PARSER = {
7
+ 'git' => {
8
+ nil => proc do
9
+ OptionParser.new do |opts|
10
+ opts.on('-c', '--config-env=NAME=VALUE') { |v|
11
+ name, envvar = v.split('=')
12
+ @hash['--config-env'] ||= {}
13
+ @hash['--config-env'][name] = envvar
14
+ }
15
+ end
16
+ end,
17
+ },
18
+ 'config' => {
19
+ nil => proc do
20
+ OptionParser.new do |opts|
21
+ end
22
+ end,
23
+ },
24
+ 'status' => {
25
+ nil => proc do
26
+ OptionParser.new do |opts|
27
+ end
28
+ end,
29
+ },
30
+ 'add' => {
31
+ nil => proc do
32
+ OptionParser.new do |opts|
33
+ end
34
+ end,
35
+ },
36
+ 'commit' => {
37
+ nil => proc do
38
+ OptionParser.new do |opts|
39
+ opts.on('-m', '--message') { |v| @hash['--message'] = v }
40
+ end
41
+ end,
42
+ },
43
+ 'fetch' => {
44
+ nil => proc do
45
+ OptionParser.new do |opts|
46
+ end
47
+ end,
48
+ },
49
+ 'branch' => {
50
+ nil => proc do
51
+ OptionParser.new do |opts|
52
+ end
53
+ end
54
+ },
55
+ 'checkout' => {
56
+ nil => proc do
57
+ OptionParser.new do |opts|
58
+ end
59
+ end
60
+ },
61
+ 'merge' => {
62
+ nil => proc do
63
+ OptionParser.new do |opts|
64
+ opts.on('-m', '--message') { |v| @hash['--message'] = v }
65
+ opts.on('--no-commit') { |v| @hash['--no-commit'] = '' }
66
+ end
67
+ end
68
+ },
69
+ 'log' => {
70
+ nil => proc do
71
+ OptionParser.new do |opts|
72
+ end
73
+ end
74
+ },
75
+ 'diff' => {
76
+ nil => proc do
77
+ OptionParser.new do |opts|
78
+ end
79
+ end
80
+ },
81
+ 'submodule' => {
82
+ nil => proc do
83
+ OptionParser.new do |opts|
84
+ end
85
+ end,
86
+ 'add' => proc do
87
+ OptionParser.new do |opts|
88
+ end
89
+ end,
90
+ 'init' => proc do
91
+ OptionParser.new do |opts|
92
+ end
93
+ end,
94
+ 'update' => proc do
95
+ OptionParser.new do |opts|
96
+ end
97
+ end
98
+ },
99
+ 'ls-tree' => {
100
+ nil => proc do
101
+ OptionParser.new do |opts|
102
+ end
103
+ end
104
+ },
105
+ 'rev-parse' => {
106
+ nil => proc do
107
+ OptionParser.new do |opts|
108
+ end
109
+ end
110
+ },
111
+ }
112
+
113
+ def parser_proc(cmd, sub_cmd, *args)
114
+ OPTION_PARSER[cmd][sub_cmd]
115
+ end
116
+ end
117
+ end
118
+ end
@@ -1,104 +1,67 @@
1
1
  module Coaster
2
2
  module Git
3
3
  class Repository
4
- class << self
5
- def option_parser(options)
6
- case options
7
- when Hash then hash_option_parser(options)
8
- when Array, Set then options.map{|o| option_parser(o)}.join(' ')
9
- else options
10
- end
11
- end
12
-
13
- def hash_option_parser(options)
14
- opts = []
15
-
16
- # multiple options can be passed by set
17
- options.map do |k, v|
18
- if v.is_a?(Set)
19
- v.each {|set_v| opts << [k, set_v]}
20
- else
21
- opts << [k, v]
22
- end
23
- end
24
-
25
- parsed = opts.map do |k, v|
26
- v = case v
27
- when Hash then v.map{|vk,vv| "#{vk}=#{vv}"}.join(',')
28
- when Array then v.join(',')
29
- else v || ''
30
- end
31
- v = v.strip
32
- if k.start_with?('--')
33
- "#{k}#{v.length > 0 ? "=#{v}" : ''}" # ex, --config-env=<name>=<envvar>
34
- else
35
- "#{k} #{v.length > 0 ? "#{v}" : ''}" # ex, -c <name>=<value>
36
- end
37
- end
38
-
39
- parsed.join(' ')
40
- end
41
-
42
- def run_cmd(path, command)
43
- puts "#{path}: #{command}"
44
- stdout, stderr, status = Open3.capture3(command, chdir: path)
45
- if status.success?
46
- puts " ↳ success: #{stdout}"
47
- stdout
48
- else
49
- raise "Error executing command: #{command}\n ↳ #{stderr}"
50
- end
51
- end
52
-
53
- def create(path)
54
- run_cmd(path.split('/')[0..-2].join('/'), "git init #{path}")
55
- run_cmd(path, "git commit --allow-empty -m 'initial commit'")
56
- new(path)
57
- end
58
- end
4
+ include Coaster::Git
59
5
 
60
6
  attr_reader :path
61
7
 
62
8
  def initialize(path)
63
9
  @path = path
64
- @sha = current_sha
65
10
  end
66
11
 
67
- def run_cmd(command)
68
- self.class.run_cmd(path, command)
12
+ def run_cmd(command, path: nil)
13
+ super(path || @path, command)
14
+ end
15
+
16
+ def with_git_options(*args, **options, &block)
17
+ @git_options = Options.new('git', *args, **options)
18
+ yield
19
+ @git_options = nil
69
20
  end
70
21
 
71
- def run_git_cmd(command, *options)
72
- cmd = "git #{self.class.option_parser(options)} #{command}"
22
+ def run_git_cmd(command, *args, **options)
23
+ opts = Options.new(command, *args, **options)
24
+ cmd = "git #{@git_options} #{Array.wrap(command).join(' ')} #{opts}"
73
25
  run_cmd(cmd)
74
26
  end
75
27
 
76
28
  def add(*paths, **options)
77
- run_git_cmd("add #{self.class.hash_option_parser(options)} #{paths.join(' ')}")
29
+ opts = Options.new('add', **options)
30
+ run_git_cmd("add", *paths, opts)
78
31
  end
79
32
 
80
- def commit(message)
81
- run_git_cmd("commit -m \"#{message}\"")
33
+ def commit(*args, **options)
34
+ opts = Options.new('commit', *args, **options)
35
+ opts['--message'] ||= "no message"
36
+ run_git_cmd("commit", opts)
82
37
  end
83
38
 
84
- def branch(name)
85
- run_git_cmd("branch #{name}")
39
+ def branch(*args, **options)
40
+ run_git_cmd("branch", *args, **options)
86
41
  end
87
42
 
88
- def checkout(name)
89
- run_git_cmd("checkout #{name}")
43
+ def checkout(*args, **options)
44
+ run_git_cmd("checkout", *args, **options)
90
45
  end
91
46
 
92
- def submodule_add!(path, url, git_options: {})
93
- run_git_cmd("submodule add #{url} #{path}", **git_options)
47
+ def submodule_add!(repo, path, *args, **options)
48
+ run_git_cmd(["submodule", 'add'], repo, path, *args, **options)
94
49
  end
95
50
 
96
- def submodule_init!(path)
97
- run_git_cmd("submodule init #{path}")
51
+ def submodule_init!(*paths)
52
+ run_git_cmd(["submodule", "init"], *paths)
98
53
  end
99
54
 
100
- def submodule_update!(*paths, options: {})
101
- run_git_cmd("submodule update #{self.class.option_parser(options)} #{paths.join(' ')}")
55
+ def submodule_update!(*paths, **options)
56
+ run_git_cmd(["submodule", "update"], *paths, **options)
57
+ end
58
+
59
+ def fetch(*args, **options)
60
+ run_git_cmd("fetch", *args, **options)
61
+ end
62
+
63
+ def status(*args, **options)
64
+ run_git_cmd("status", *args, **options)
102
65
  end
103
66
 
104
67
  def current_sha
@@ -117,33 +80,59 @@ module Coaster
117
80
  end.to_h
118
81
  end
119
82
 
120
- def merge(pointer)
83
+ def merge(pointer, *args, **options)
84
+ opts = Options.new('merge', *args, **options)
121
85
  pointers = pointers(pointer).join(',')
122
- puts "#{path} merged deploy: #{pointers}"
123
- run_git_cmd("merge #{pointer} --commit -m \"merged deploy: #{pointers}\"")
86
+ puts "[MERGE] #{path} #{pointers} #{options}"
87
+ opts['--message'] ||= "Merge #{pointers}"
88
+ run_git_cmd("merge #{pointer} #{opts}")
124
89
  end
125
90
 
126
91
  def submodule_sha(path, pointer: nil)
127
- pointer ||= @sha
92
+ pointer ||= current_sha
128
93
  run_git_cmd("ls-tree #{pointer} #{path}").split(' ')[2]
129
94
  end
130
95
 
96
+ def merge_without_submodules
97
+ run_git_cmd('config merge.ours.name "Keep ours merge driver"')
98
+ run_git_cmd('config merge.ours.driver true')
99
+ ga_file = File.join(@path, '.gitattributes')
100
+ run_cmd("touch #{ga_file}")
101
+ ga_lines = File.read(ga_file).split("\n")
102
+ ga_lines_appended = ga_lines + submodules.keys.map{|sb_path| "#{sb_path} merge=ours" }
103
+ File.open(ga_file, 'w') do |f|
104
+ f.puts ga_lines_appended.join("\n")
105
+ end
106
+ add('.')
107
+ commit
108
+ yield
109
+ File.open(ga_file, 'w') do |f|
110
+ f.puts ga_lines.join("\n")
111
+ end
112
+ add('.')
113
+ commit
114
+ end
115
+
131
116
  def deep_merge(pointer)
117
+ puts "[DEEP_MERGE] #{path} #{pointer}"
132
118
  submodules.values.each do |submodule|
133
119
  sm_sha = submodule_sha(submodule.path, pointer: pointer)
134
120
  submodule.merge(sm_sha)
135
121
  end
136
- merge(pointer)
122
+ merge_without_submodules do
123
+ merge(pointer)
124
+ end
137
125
  end
138
126
 
139
127
  def pointers(sha)
140
128
  run_git_cmd("branch --contains #{sha}").split("\n").map do |br|
141
- (br.start_with?('*') ? br[2..-1] : br).strip
142
- end
129
+ br = (br.start_with?('*') ? br[2..-1] : br).strip
130
+ br.match?(/^\(.*\)$/) ? nil : br
131
+ end.compact
143
132
  end
144
133
 
145
134
  def remove
146
- self.class.run_cmd(path.split('/')[0..-2].join('/'), "rm -rf #{path}")
135
+ run_cmd("rm -rf #{path}", path: path.split('/')[0..-2].join('/'))
147
136
  end
148
137
  end
149
138
  end
data/lib/coaster/git.rb CHANGED
@@ -2,8 +2,28 @@ require 'open3'
2
2
 
3
3
  module Coaster
4
4
  module Git
5
+ def run_cmd(path, command)
6
+ puts "#{path}: #{command}"
7
+ stdout, stderr, status = Open3.capture3(command, chdir: path)
8
+ if status.success?
9
+ puts " ↳ success: #{stdout.split("\n").join("\n ")}"
10
+ stdout
11
+ else
12
+ raise "Error executing command\nPATH: #{path}\nCMD: #{command}\nSTDERR:\n ↳ #{stderr.split("\n").join("\n ")}\nSTDOUT:\n ↳ #{stdout.split("\n").join("\n ")}"
13
+ end
14
+ end
5
15
 
16
+ class << self
17
+ include Coaster::Git
18
+
19
+ def create(path)
20
+ run_cmd(path.split('/')[0..-2].join('/'), "git init #{path}")
21
+ run_cmd(path, "git commit --allow-empty -m 'initial commit'")
22
+ Repository.new(path)
23
+ end
24
+ end
6
25
  end
7
26
  end
8
27
 
28
+ require 'coaster/git/options'
9
29
  require 'coaster/git/repository'
@@ -1,3 +1,3 @@
1
1
  module Coaster
2
- VERSION = '1.3.32'
2
+ VERSION = '1.3.34'
3
3
  end
data/test/test_git.rb CHANGED
@@ -4,53 +4,5 @@ require 'coaster/git'
4
4
 
5
5
  module Coaster
6
6
  class TestGit < Minitest::Test
7
- def setup
8
- super
9
- @test_repo_root = File.expand_path('../../tmp/test_repo', __FILE__)
10
- FileUtils.rm_rf(@test_repo_root)
11
- FileUtils.mkdir_p(@test_repo_root)
12
- @beta = Git::Repository.create(File.join(@test_repo_root, 'beta'))
13
- @beta.run_cmd('echo "hello beta" > README.md')
14
- @beta.add('.')
15
- @beta.run_git_cmd('commit -m "hello"')
16
- @beta.branch('beta_feature')
17
- @beta.checkout('beta_feature')
18
- @beta.run_cmd('echo "beta_feature" >> README.md')
19
- @beta.add('.')
20
- @beta.run_git_cmd('commit -m "beta_feature"')
21
- @beta.run_git_cmd('checkout main')
22
-
23
- @alpha = Git::Repository.create(File.join(@test_repo_root, 'alpha'))
24
- @alpha.submodule_add!('sb/beta', @beta.path, git_options: {'-c' => {'protocol.file.allow' => 'always'}})
25
- @alpha.submodule_update!('sb/beta')
26
- @alpha.run_cmd('echo "hello alpha" > README.md')
27
- @alpha.add('.')
28
- @alpha.run_git_cmd('commit -m "hello"')
29
- end
30
-
31
- def teardown
32
- FileUtils.rm_rf(@test_repo_root)
33
- super
34
- end
35
-
36
- def test_git_deep_merge
37
- assert_equal "hello alpha\n", @alpha.run_cmd('cat README.md')
38
- assert_equal "hello beta\n", @alpha.run_cmd('cat sb/beta/README.md')
39
-
40
- @alpha.branch('alpha_feature')
41
- @alpha.checkout('alpha_feature')
42
- @alpha.run_cmd('echo "alpha_feature" >> README.md')
43
- @alpha.submodules['sb/beta'].run_git_cmd('checkout beta_feature')
44
- @alpha.add('.')
45
- @alpha.run_git_cmd('commit -m "alpha_feature"')
46
- assert_equal "README.md\nsb/beta\n", @alpha.run_git_cmd('diff --name-only HEAD~1 HEAD')
47
-
48
- @alpha.checkout('main')
49
- @alpha.submodule_update!
50
- assert_equal "hello beta\n", @alpha.run_cmd('cat sb/beta/README.md')
51
- @alpha.deep_merge('alpha_feature')
52
- assert_equal "hello alpha\nalpha_feature\n", @alpha.run_cmd('cat README.md')
53
- assert_equal "hello beta\nbeta_feature\n", @alpha.run_cmd('cat sb/beta/README.md')
54
- end
55
7
  end
56
8
  end
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+ require 'minitest/autorun'
3
+ require 'coaster/git'
4
+
5
+ module Coaster
6
+ module Git
7
+ class TestOptions < Minitest::Test
8
+ def test_options_to_s
9
+ # assert_equal '-a b -c d ', Options.new(nil, '-a' => 'b', '-c' => 'd').to_s
10
+ assert_equal '--no-commit ', Options.new(nil, '--no-commit').to_s
11
+ assert_equal '-c a=b', Options.new(nil, '-c' => {'a' => 'b'}).to_s
12
+ assert_equal '-c a=b -c c=d', Options.new(nil, '-c' => {'a' => 'b', 'c' => 'd'}).to_s
13
+ assert_equal '-c a=b -c c=d', Options.new(nil, '-c' => Set[{'a' => 'b'}, {'c' => 'd'}]).to_s
14
+ assert_equal '--config=a=b,c=d', Options.new(nil, '--config' => [Set['a', 'b'], Set['c', 'd']]).to_s
15
+ assert_equal '--config=a=b --config=c=d', Options.new(nil, '--config' => Set[{'a' => 'b'}, {'c' => 'd'}]).to_s
16
+ assert_equal '-c a=b -- aaa bbb', Options.new(nil, '-c' => {'a' => 'b'}, '--' => ['aaa', 'bbb']).to_s
17
+ end
18
+
19
+ def test_options_to_h
20
+ opts = Options.new('git', '-c' => {'b' => 1}, '--config-env' => 'd=1')
21
+ assert_equal({"--config-env"=>{"b"=>"1", "d"=>"1"}}, opts.to_h)
22
+ assert_equal('--config-env=b=1 --config-env=d=1 ', opts.to_s)
23
+ opts = Options.new('merge', '--no-commit')
24
+ assert_equal({"--no-commit" => ''}, opts.to_h)
25
+ assert_equal('--no-commit ', opts.to_s)
26
+ opts = Options.new('git', {'-c' => {'a' => 'b'}, '--' => ['aaa', 'bbb']})
27
+ assert_equal('-c a=b -- aaa bbb', opts.to_s)
28
+ assert_equal({"--config-env" => {"a" => "b"}, '--' => ['aaa', 'bbb']}, opts.to_h)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,59 @@
1
+ require 'test_helper'
2
+ require 'minitest/autorun'
3
+ require 'coaster/git'
4
+
5
+ module Coaster
6
+ class TestGitRepository < Minitest::Test
7
+ def setup
8
+ super
9
+ @test_repo_root = File.expand_path('../../tmp/test_repo', __FILE__)
10
+ FileUtils.rm_rf(@test_repo_root)
11
+ FileUtils.mkdir_p(@test_repo_root)
12
+ @beta = Coaster::Git.create(File.join(@test_repo_root, 'beta'))
13
+ @beta.run_cmd('echo "hello beta" > README.md')
14
+ @beta.add('.')
15
+ @beta.run_git_cmd('commit -m "hello"')
16
+ @beta.branch('beta_feature')
17
+ @beta.checkout('beta_feature')
18
+ @beta.run_cmd('echo "beta_feature" >> README.md')
19
+ @beta.add('.')
20
+ @beta.run_git_cmd('commit -m "beta_feature"')
21
+ @beta.run_git_cmd('checkout main')
22
+
23
+ @alpha = Coaster::Git.create(File.join(@test_repo_root, 'alpha'))
24
+ @alpha.with_git_options({'-c' => {'protocol.file.allow' => 'always'}}) do
25
+ @alpha.submodule_add!(@beta.path, 'sb/beta')
26
+ end
27
+ @alpha.submodule_update!('sb/beta')
28
+ @alpha.run_cmd('echo "hello alpha" > README.md')
29
+ @alpha.add('.')
30
+ @alpha.run_git_cmd('commit -m "hello"')
31
+ end
32
+
33
+ def test_git_deep_merge
34
+ assert_equal "hello alpha\n", @alpha.run_cmd('cat README.md')
35
+ assert_equal "hello beta\n", @alpha.run_cmd('cat sb/beta/README.md')
36
+
37
+ @alpha.branch('alpha_feature')
38
+ @alpha.checkout('alpha_feature')
39
+ @alpha.run_cmd('echo "alpha_feature" >> README.md')
40
+ @alpha.submodules['sb/beta'].run_git_cmd('checkout beta_feature')
41
+ @alpha.add('.')
42
+ @alpha.run_git_cmd('commit -m "alpha_feature"')
43
+ assert_equal "README.md\nsb/beta\n", @alpha.run_git_cmd('diff --name-only HEAD~1 HEAD')
44
+
45
+ @alpha.checkout('main')
46
+ @alpha.submodule_update!
47
+ @alpha.submodules['sb/beta'].run_cmd('echo "main new commit" >> README2.md')
48
+ @alpha.submodules['sb/beta'].add('.')
49
+ @alpha.submodules['sb/beta'].run_git_cmd('commit -m "main new commit"')
50
+ @alpha.add('.')
51
+ @alpha.run_git_cmd('commit -m "main new commit"')
52
+ assert_equal "hello beta\n", @alpha.run_cmd('cat sb/beta/README.md')
53
+
54
+ @alpha.deep_merge('alpha_feature')
55
+ assert_equal "hello alpha\nalpha_feature\n", @alpha.run_cmd('cat README.md')
56
+ assert_equal "hello beta\nbeta_feature\n", @alpha.run_cmd('cat sb/beta/README.md')
57
+ end
58
+ end
59
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coaster
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.32
4
+ version: 1.3.34
5
5
  platform: ruby
6
6
  authors:
7
7
  - buzz jung
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-30 00:00:00.000000000 Z
11
+ date: 2023-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oj
@@ -190,6 +190,7 @@ files:
190
190
  - Rakefile
191
191
  - bin/coaster
192
192
  - lib/coaster.rb
193
+ - lib/coaster/cmd_options.rb
193
194
  - lib/coaster/core_ext.rb
194
195
  - lib/coaster/core_ext/array.rb
195
196
  - lib/coaster/core_ext/date.rb
@@ -200,6 +201,7 @@ files:
200
201
  - lib/coaster/core_ext/standard_error/raven.rb
201
202
  - lib/coaster/core_ext/standard_error/sentry.rb
202
203
  - lib/coaster/git.rb
204
+ - lib/coaster/git/options.rb
203
205
  - lib/coaster/git/repository.rb
204
206
  - lib/coaster/rails_ext.rb
205
207
  - lib/coaster/rails_ext/backtrace_cleaner.rb
@@ -212,6 +214,8 @@ files:
212
214
  - test/support/schema.rb
213
215
  - test/test_backtrace.rb
214
216
  - test/test_git.rb
217
+ - test/test_git_options.rb
218
+ - test/test_git_repository.rb
215
219
  - test/test_helper.rb
216
220
  - test/test_month.rb
217
221
  - test/test_object_translation.rb
@@ -249,6 +253,8 @@ test_files:
249
253
  - test/support/schema.rb
250
254
  - test/test_backtrace.rb
251
255
  - test/test_git.rb
256
+ - test/test_git_options.rb
257
+ - test/test_git_repository.rb
252
258
  - test/test_helper.rb
253
259
  - test/test_month.rb
254
260
  - test/test_object_translation.rb