coaster 1.3.32 → 1.3.33

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ade22d0084d98fb4169986fce083c991fdf00b2d5e95a4fec2f2fb2a03b0ffbb
4
- data.tar.gz: 43bedfaf5b382b1817bac45efd7ed1416f75696dc625faecf7be4f680e532985
3
+ metadata.gz: 684eb09e07b2184d93f7dd1ba8ef28df6d75c29bbaac226e4e513da16e0d22f5
4
+ data.tar.gz: 5479f75d9364214c164b022a7fc2fb96802c7258663909b16afbdcb75d45ad8e
5
5
  SHA512:
6
- metadata.gz: 0bc77179288fb0841bf49646c799f75793f03e941032ee04fca006113a516f14f984cf07222a56e4e93fd49f45b3469726e84428bca177b86341d4cf3dbe1928
7
- data.tar.gz: 144d370265cfc0ab7618edd71f66239269c039710a3192579d53badefa67c54e49db75646bfa437af91167a73243f0a6bbb25039c46b9be504110797a86a8329
6
+ metadata.gz: 58f78f7427f2069f3c69389f23dfec8140c365f0d9817e8ee71cc00db978365344d2f4b109acc9268889150509ce63ae56f6b02f087915815ac6d91e5b91e183
7
+ data.tar.gz: 3e70c07f05f01312fd95a33b711ea7bd4289db6e4e447392250a8c22568ed8af236456c80f3846ae530621e1038320b70a5f11d74d269a99f13b0bed32b81879
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,159 @@
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_accessor :repository
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
+ if @args.first.is_a?(self.class)
115
+ options = @args.first.to_h.merge(options)
116
+ end
117
+ if @args.last.is_a?(self.class)
118
+ options = @args.last.to_h.merge(options)
119
+ end
120
+ options['--'] ||= []
121
+ options['--'] += @remain_args if @remain_args
122
+ @options = options
123
+ @args << self.class.options_to_s(options).strip
124
+ @str = @args.join(' ')
125
+ end
126
+
127
+ def parser_proc(*args)
128
+ raise 'Not implemented'
129
+ end
130
+
131
+ def parser
132
+ parser_proc = parser_proc(@cmd, @sub_cmd)
133
+ instance_exec(&parser_proc)
134
+ end
135
+
136
+ def to_h
137
+ return @hash if defined?(@hash)
138
+ @hash = {}
139
+ remain_args = parser.parse!(@str.split(' '))
140
+ @hash['--'] = remain_args if remain_args.any?
141
+ @hash
142
+ end
143
+ delegate :[], :[]=, :key?, :map, :each, to: :to_h
144
+
145
+ def merge(*args, **options)
146
+ if args.first.is_a?(CmdOptions)
147
+ other = args.shift
148
+ else
149
+ other = self.class.new([@cmd, @sub_cmd], *args, **options)
150
+ end
151
+ self.class.new(to_h.merge(other.to_h))
152
+ end
153
+
154
+ def to_s
155
+ return self.class.options_h_to_s(@hash) if defined?(@hash)
156
+ @str
157
+ end
158
+ end
159
+ 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', *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,57 @@ 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
+ end
113
+
131
114
  def deep_merge(pointer)
115
+ puts "[DEEP_MERGE] #{path} #{pointer}"
132
116
  submodules.values.each do |submodule|
133
117
  sm_sha = submodule_sha(submodule.path, pointer: pointer)
134
118
  submodule.merge(sm_sha)
135
119
  end
136
- merge(pointer)
120
+ merge_without_submodules do
121
+ merge(pointer)
122
+ end
137
123
  end
138
124
 
139
125
  def pointers(sha)
140
126
  run_git_cmd("branch --contains #{sha}").split("\n").map do |br|
141
- (br.start_with?('*') ? br[2..-1] : br).strip
142
- end
127
+ br = (br.start_with?('*') ? br[2..-1] : br).strip
128
+ br.match?(/^\(.*\)$/) ? nil : br
129
+ end.compact
143
130
  end
144
131
 
145
132
  def remove
146
- self.class.run_cmd(path.split('/')[0..-2].join('/'), "rm -rf #{path}")
133
+ run_cmd("rm -rf #{path}", path: path.split('/')[0..-2].join('/'))
147
134
  end
148
135
  end
149
136
  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.33'
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,64 @@
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 teardown
34
+ FileUtils.rm_rf(@test_repo_root)
35
+ super
36
+ end
37
+
38
+ def test_git_deep_merge
39
+ assert_equal "hello alpha\n", @alpha.run_cmd('cat README.md')
40
+ assert_equal "hello beta\n", @alpha.run_cmd('cat sb/beta/README.md')
41
+
42
+ @alpha.branch('alpha_feature')
43
+ @alpha.checkout('alpha_feature')
44
+ @alpha.run_cmd('echo "alpha_feature" >> README.md')
45
+ @alpha.submodules['sb/beta'].run_git_cmd('checkout beta_feature')
46
+ @alpha.add('.')
47
+ @alpha.run_git_cmd('commit -m "alpha_feature"')
48
+ assert_equal "README.md\nsb/beta\n", @alpha.run_git_cmd('diff --name-only HEAD~1 HEAD')
49
+
50
+ @alpha.checkout('main')
51
+ @alpha.submodule_update!
52
+ @alpha.submodules['sb/beta'].run_cmd('echo "main new commit" >> README2.md')
53
+ @alpha.submodules['sb/beta'].add('.')
54
+ @alpha.submodules['sb/beta'].run_git_cmd('commit -m "main new commit"')
55
+ @alpha.add('.')
56
+ @alpha.run_git_cmd('commit -m "main new commit"')
57
+ assert_equal "hello beta\n", @alpha.run_cmd('cat sb/beta/README.md')
58
+
59
+ @alpha.deep_merge('alpha_feature')
60
+ assert_equal "hello alpha\nalpha_feature\n", @alpha.run_cmd('cat README.md')
61
+ assert_equal "hello beta\nbeta_feature\n", @alpha.run_cmd('cat sb/beta/README.md')
62
+ end
63
+ end
64
+ 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.33
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-08 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