dapp 0.7.36 → 0.8.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/config/en/common.yml +1 -3
  3. data/config/en/net_status.yml +7 -9
  4. data/lib/dapp.rb +3 -1
  5. data/lib/dapp/artifact.rb +7 -2
  6. data/lib/dapp/build/stage/after_setup_artifact.rb +1 -1
  7. data/lib/dapp/build/stage/artifact_base.rb +1 -1
  8. data/lib/dapp/build/stage/artifact_default.rb +24 -43
  9. data/lib/dapp/build/stage/base.rb +35 -31
  10. data/lib/dapp/build/stage/build_artifact.rb +1 -1
  11. data/lib/dapp/build/stage/from.rb +1 -1
  12. data/lib/dapp/build/stage/ga_archive_dependencies.rb +10 -1
  13. data/lib/dapp/build/stage/ga_artifact_patch.rb +2 -2
  14. data/lib/dapp/build/stage/ga_base.rb +18 -5
  15. data/lib/dapp/build/stage/ga_dependencies_base.rb +1 -1
  16. data/lib/dapp/build/stage/ga_latest_patch.rb +10 -2
  17. data/lib/dapp/build/stage/import_artifact.rb +1 -1
  18. data/lib/dapp/build/stage/install/ga_pre_install_patch_dependencies.rb +0 -4
  19. data/lib/dapp/build/stage/install/install.rb +1 -1
  20. data/lib/dapp/build/stage/mod/logging.rb +2 -2
  21. data/lib/dapp/build/stage/setup/ga_post_setup_patch.rb +0 -4
  22. data/lib/dapp/build/stage/setup/ga_post_setup_patch_dependencies.rb +2 -2
  23. data/lib/dapp/build/stage/setup/ga_pre_setup_patch.rb +0 -4
  24. data/lib/dapp/build/stage/setup/setup.rb +1 -1
  25. data/lib/dapp/builder/base.rb +0 -7
  26. data/lib/dapp/builder/chef.rb +67 -408
  27. data/lib/dapp/builder/chef/berksfile.rb +78 -69
  28. data/lib/dapp/builder/chef/cookbook.rb +257 -0
  29. data/lib/dapp/builder/chef/cookbook_metadata.rb +54 -52
  30. data/lib/dapp/builder/none.rb +0 -6
  31. data/lib/dapp/cli/build.rb +8 -1
  32. data/lib/dapp/cli/stage_image.rb +1 -1
  33. data/lib/dapp/config/directive/base.rb +1 -3
  34. data/lib/dapp/config/directive/git_artifact_remote.rb +3 -6
  35. data/lib/dapp/dimg.rb +13 -14
  36. data/lib/dapp/dimg/path.rb +1 -9
  37. data/lib/dapp/error/tar_writer.rb +6 -0
  38. data/lib/dapp/git_artifact.rb +136 -37
  39. data/lib/dapp/git_repo/base.rb +44 -28
  40. data/lib/dapp/git_repo/own.rb +11 -7
  41. data/lib/dapp/git_repo/remote.rb +8 -45
  42. data/lib/dapp/image/docker.rb +9 -11
  43. data/lib/dapp/image/stage.rb +1 -1
  44. data/lib/dapp/project/chef.rb +2 -7
  45. data/lib/dapp/project/command/stages/cleanup_local.rb +1 -2
  46. data/lib/dapp/project/command/stages/cleanup_repo.rb +1 -2
  47. data/lib/dapp/version.rb +2 -2
  48. metadata +4 -3
  49. data/lib/dapp/build/stage/setup/chef_cookbooks.rb +0 -36
@@ -34,12 +34,6 @@ module Dapp
34
34
 
35
35
  def build_artifact_checksum
36
36
  end
37
-
38
- def chef_cookbooks(_image)
39
- end
40
-
41
- def chef_cookbooks_checksum
42
- end
43
37
  end # Base
44
38
  end # Builder
45
39
  end # Dapp
@@ -40,7 +40,14 @@ BANNER
40
40
  proc: proc { |v| v.to_sym },
41
41
  in: [nil, :from, :before_install, :before_install_artifact, :g_a_archive, :g_a_pre_install_patch, :install,
42
42
  :g_a_post_install_patch, :after_install_artifact, :before_setup, :before_setup_artifact, :g_a_pre_setup_patch,
43
- :chef_cookbooks, :setup, :g_a_post_setup_patch, :after_setup_artifact, :g_a_latest_patch, :docker_instructions]
43
+ :setup, :g_a_post_setup_patch, :after_setup_artifact, :g_a_latest_patch, :docker_instructions]
44
+
45
+ option :introspect_artifact_stage,
46
+ long: '--introspect-artifact-stage STAGE',
47
+ proc: proc { |v| v.to_sym },
48
+ in: [nil, :from, :before_install, :before_install_artifact, :g_a_archive, :g_a_pre_install_patch, :install,
49
+ :g_a_post_install_patch, :after_install_artifact, :before_setup, :before_setup_artifact, :g_a_pre_setup_patch,
50
+ :setup, :after_setup_artifact, :g_a_artifact_patch, :build_artifact]
44
51
 
45
52
  option :ssh_key,
46
53
  long: '--ssh-key SSH_KEY',
@@ -18,7 +18,7 @@ BANNER
18
18
  default: :docker_instructions,
19
19
  in: [:from, :before_install, :before_install_artifact, :g_a_archive, :g_a_pre_install_patch, :install,
20
20
  :g_a_post_install_patch, :after_install_artifact, :before_setup, :before_setup_artifact, :g_a_pre_setup_patch,
21
- :chef_cookbooks, :setup, :g_a_post_setup_patch, :after_setup_artifact, :g_a_latest_patch, :docker_instructions]
21
+ :setup, :g_a_post_setup_patch, :after_setup_artifact, :g_a_latest_patch, :docker_instructions]
22
22
  end
23
23
  end
24
24
  end
@@ -14,9 +14,7 @@ module Dapp
14
14
  end
15
15
 
16
16
  def path_format(path)
17
- path = path.to_s
18
- path = path.chomp('/') unless path == '/'
19
- path
17
+ path.to_s.chomp('/')
20
18
  end
21
19
  end
22
20
  end
@@ -6,6 +6,8 @@ module Dapp
6
6
  attr_reader :_url, :_name, :_branch, :_commit
7
7
 
8
8
  def initialize(url, **kwargs, &blk)
9
+ raise Error::Config, code: :git_artifact_remote_unsupported_protocol, data: { url: url } unless %w(http https).include? URI(url.to_s).scheme
10
+
9
11
  @_url = url
10
12
  @_name = url.gsub(%r{.*?([^\/ ]+)\.git}, '\\1')
11
13
 
@@ -26,7 +28,7 @@ module Dapp
26
28
  attr_accessor :_url, :_name, :_branch, :_commit
27
29
 
28
30
  def _artifact_options
29
- super.merge(name: _name, branch: _branch, commit: _commit)
31
+ super.merge(name: _name, branch: _branch)
30
32
  end
31
33
 
32
34
  protected
@@ -38,11 +40,6 @@ module Dapp
38
40
  def commit(value)
39
41
  @_commit = value
40
42
  end
41
-
42
- def validate!
43
- super
44
- raise Error::Config, code: :git_artifact_remote_branch_with_commit if !_branch.nil? && !_commit.nil?
45
- end
46
43
  end
47
44
 
48
45
  protected
@@ -31,7 +31,7 @@ module Dapp
31
31
  builder.before_build_check
32
32
  last_stage.build!
33
33
  ensure
34
- last_stage.save_in_cache! if last_stage.image.built? || dev_mode?
34
+ after_stages_build!
35
35
  end
36
36
  end
37
37
  end
@@ -40,6 +40,12 @@ module Dapp
40
40
  cleanup_tmp
41
41
  end
42
42
 
43
+ def after_stages_build!
44
+ return unless last_stage.image.built? || dev_mode?
45
+ last_stage.save_in_cache!
46
+ artifacts.each { |artifact| artifact.last_stage.save_in_cache! }
47
+ end
48
+
43
49
  def tag!(tag)
44
50
  project.lock("#{project.name}.images", readonly: true) do
45
51
  dimg_name = config._name
@@ -116,7 +122,7 @@ module Dapp
116
122
  end
117
123
 
118
124
  def run(docker_options, command)
119
- cmd = "docker run #{[docker_options, last_stage.image.name, command].flatten.compact.join(' ')}"
125
+ cmd = "docker run #{[docker_options, last_stage.image.built_id, command].flatten.compact.join(' ')}"
120
126
  if project.dry_run?
121
127
  project.log(cmd)
122
128
  else
@@ -158,21 +164,14 @@ module Dapp
158
164
  end
159
165
 
160
166
  def cleanup_tmp
161
- # В tmp-директории могли остаться файлы, владельцами которых мы не являемся.
162
- # Такие файлы могут попасть туда при экспорте файлов артефакта.
163
- # Чтобы от них избавиться — запускаем docker-контейнер под root-пользователем
164
- # и удаляем примонтированную tmp-директорию.
165
- cmd = "".tap do |cmd|
166
- cmd << "docker run --rm"
167
- cmd << " --volume #{tmp_base_dir}:#{tmp_base_dir}"
168
- cmd << " ubuntu:16.04"
169
- cmd << " rm -rf #{tmp_path}"
170
- end
171
- project.shellout! cmd
172
-
167
+ FileUtils.rm_rf(tmp_path)
173
168
  artifacts.each(&:cleanup_tmp)
174
169
  end
175
170
 
171
+ def stage_should_be_introspected?(name)
172
+ project.cli_options[:introspect_stage] == name
173
+ end
174
+
176
175
  protected
177
176
 
178
177
  def should_be_built?
@@ -7,16 +7,8 @@ module Dapp
7
7
  make_path(project.path, *path).expand_path
8
8
  end
9
9
 
10
- def local_cookbook_path(*path)
11
- make_path(project.local_cookbook_path, *path)
12
- end
13
-
14
- def tmp_base_dir
15
- File.expand_path(project.cli_options[:tmp_dir_prefix] || '/tmp')
16
- end
17
-
18
10
  def tmp_path(*path)
19
- @tmp_path ||= Dir.mktmpdir('dapp-', tmp_base_dir)
11
+ @tmp_path ||= Dir.mktmpdir('dapp-', project.cli_options[:tmp_dir_prefix] || '/tmp')
20
12
  make_path(@tmp_path, *path).expand_path.tap { |p| p.parent.mkpath }
21
13
  end
22
14
 
@@ -0,0 +1,6 @@
1
+ module Dapp
2
+ module Error
3
+ # TarWriter
4
+ class TarWriter < Base; end
5
+ end
6
+ end
@@ -14,7 +14,14 @@ module Dapp
14
14
  @commit = commit
15
15
 
16
16
  @to = to
17
- @cwd = (cwd.nil? || cwd.empty? || cwd == '/') ? '' : File.expand_path(File.join('/', cwd, '/'))[1..-1]
17
+ @cwd = begin
18
+ if cwd.nil? || cwd.empty?
19
+ ''
20
+ else
21
+ cwd = File.expand_path(File.join('/', cwd))[1..-1] # must be relative
22
+ "#{cwd.chomp('/')}/"
23
+ end
24
+ end
18
25
  @include_paths = include_paths
19
26
  @exclude_paths = exclude_paths
20
27
  @owner = owner
@@ -27,39 +34,16 @@ module Dapp
27
34
 
28
35
  [].tap do |commands|
29
36
  commands << "#{repo.dimg.project.install_bin} #{credentials.join(' ')} -d #{to}"
30
-
31
- if include_paths_or_cwd.empty? || include_paths_or_cwd.any? { |path| file_exist_in_repo?(stage.layer_commit(self), path) }
32
- commands << ["#{repo.dimg.project.git_bin} --git-dir=#{repo.container_path} archive #{stage.layer_commit(self)}:#{cwd} --prefix=/ #{include_paths.join(' ')}",
33
- "#{sudo}#{repo.dimg.project.tar_bin} -x -C #{to} #{archive_command_excludes.join(' ')}"].join(' | ')
34
- end
37
+ commands << "#{sudo}#{repo.dimg.project.tar_bin} -xf #{archive_file(stage.layer_commit(self))} -C #{to}" if any_changes?(nil, stage.layer_commit(self))
35
38
  end
36
39
  end
37
40
 
38
41
  def apply_patch_command(stage)
39
- current_commit = stage.layer_commit(self)
40
- prev_commit = stage.prev_g_a_stage.layer_commit(self)
41
-
42
- if any_changes?(prev_commit, current_commit)
43
- [["#{repo.dimg.project.git_bin} --git-dir=#{repo.container_path} #{diff_command(prev_commit, current_commit)}",
44
- "#{sudo}#{repo.dimg.project.git_bin} apply --whitespace=nowarn --directory=#{to} #{patch_command_excludes.join(' ')} --unsafe-paths"].join(' | ')]
45
- else
46
- []
47
- end
42
+ patch_command(stage.prev_g_a_stage.layer_commit(self), stage.layer_commit(self))
48
43
  end
49
44
 
50
- def archive_command_excludes
51
- exclude_paths.map { |path| %(--exclude=#{File.join('/', path)}) }
52
- end
53
-
54
- def patch_command_excludes
55
- exclude_paths.map do |path|
56
- base = File.join(to, path)
57
- path =~ /[\*\?\[\]\{\}]/ ? %(--exclude=#{base} ) : %(--exclude=#{base} --exclude=#{File.join(base, '*')})
58
- end
59
- end
60
-
61
- def any_changes?(from, to = latest_commit)
62
- diff_patches(from, to).any?
45
+ def apply_dev_patch_command(stage)
46
+ patch_command(stage.prev_g_a_stage.layer_commit(self), nil)
63
47
  end
64
48
 
65
49
  def patch_size(from, to)
@@ -79,12 +63,16 @@ module Dapp
79
63
  end
80
64
  end
81
65
 
66
+ def dev_patch_hash(stage)
67
+ Digest::SHA256.hexdigest diff_patches(stage.prev_g_a_stage.layer_commit(self), nil).map(&:to_s).join(':::')
68
+ end
69
+
82
70
  def latest_commit
83
71
  @latest_commit ||= commit || repo.latest_commit(branch)
84
72
  end
85
73
 
86
74
  def paramshash
87
- Digest::SHA256.hexdigest [full_name, to, cwd, commit, branch, *include_paths, *exclude_paths, owner, group].map(&:to_s).join(':::')
75
+ Digest::SHA256.hexdigest [full_name, to, cwd, *include_paths, *exclude_paths, owner, group].map(&:to_s).join(':::')
88
76
  end
89
77
 
90
78
  def exclude_paths(with_cwd = false)
@@ -103,6 +91,10 @@ module Dapp
103
91
  "#{repo.name}#{name ? "_#{name}" : nil}"
104
92
  end
105
93
 
94
+ def any_changes?(from, to = latest_commit)
95
+ diff_patches(from, to).any?
96
+ end
97
+
106
98
  protected
107
99
 
108
100
  attr_reader :to
@@ -112,12 +104,108 @@ module Dapp
112
104
  attr_reader :owner
113
105
  attr_reader :group
114
106
 
107
+ def patch_command(prev_commit, current_commit)
108
+ [].tap do |commands|
109
+ if any_changes?(prev_commit, current_commit)
110
+ commands << "#{sudo}#{repo.dimg.project.git_bin} apply --whitespace=nowarn --directory=#{to} --unsafe-paths " \
111
+ "#{patch_file(prev_commit, current_commit)}"
112
+ end
113
+ end
114
+ end
115
+
115
116
  def sudo
116
117
  repo.dimg.project.sudo_command(owner: owner, group: group)
117
118
  end
118
119
 
119
- def diff_command(from, to, quiet: false)
120
- "diff --binary #{'--quiet' if quiet} #{from}..#{to} #{"--relative=#{cwd}" if cwd} -- #{include_paths(true).join(' ')}"
120
+ def archive_file(commit)
121
+ create_file(repo.dimg.tmp_path('archives', archive_file_name(commit))) do |f|
122
+ Gem::Package::TarWriter.new(f) do |tar|
123
+ diff_patches(nil, commit).each do |patch|
124
+ entry = patch.delta.new_file
125
+ tar.add_file slice_cwd(entry[:path]), entry[:mode] do |tf|
126
+ tf.write repo.lookup_object(entry[:oid]).content
127
+ end
128
+ end
129
+ end
130
+ end
131
+ repo.dimg.container_tmp_path('archives', archive_file_name(commit))
132
+ rescue Gem::Package::TooLongFileName => e
133
+ raise Error::TarWriter, message: e.message
134
+ end
135
+
136
+ def slice_cwd(path)
137
+ return path if cwd.empty?
138
+ path
139
+ .reverse
140
+ .chomp(cwd.reverse)
141
+ .reverse
142
+ end
143
+
144
+ def archive_file_name(commit)
145
+ file_name(commit, 'tar')
146
+ end
147
+
148
+ def patch_file(from, to)
149
+ create_file(repo.dimg.tmp_path('patches', patch_file_name(from, to))) do |f|
150
+ diff_patches(from, to).each { |patch| f.write change_patch_new_file_path(patch) }
151
+ end
152
+ repo.dimg.container_tmp_path('patches', patch_file_name(from, to))
153
+ end
154
+
155
+ def change_patch_new_file_path(patch)
156
+ patch.to_s.lines.tap do |lines|
157
+ modify_patch_line = proc do |line_number, path_char|
158
+ action_part, path_part = lines[line_number].split
159
+ if (path_with_cwd = path_part.partition("#{path_char}/").last).start_with?(cwd)
160
+ path_with_cwd.sub(cwd, '').tap do |native_path|
161
+ expected_path = File.join(path_char, native_path)
162
+ lines[line_number] = [action_part, expected_path].join(' ') + "\n"
163
+ end
164
+ end
165
+ end
166
+
167
+ modify_patch = proc do |*modify_patch_line_args|
168
+ native_paths = modify_patch_line_args.map { |args| modify_patch_line.call(*args) }
169
+ unless (native_paths = native_paths.compact.uniq).empty?
170
+ raise Error::Build, code: :unsupported_patch_format, data: { patch: patch.to_s } unless native_paths.one?
171
+ native_path = native_paths.first
172
+ lines[0] = ['diff --git', File.join('a', native_path), File.join('b', native_path)].join(' ') + "\n"
173
+ end
174
+ end
175
+
176
+ case
177
+ when patch.delta.deleted? then modify_patch.call([3, 'a'])
178
+ when patch.delta.added? then modify_patch.call([4, 'b'])
179
+ when patch.delta.modified?
180
+ if patch_file_mode_changed?(patch)
181
+ modify_patch.call([4, 'a'], [5, 'b'])
182
+ else
183
+ modify_patch.call([2, 'a'], [3, 'b'])
184
+ end
185
+ else
186
+ raise
187
+ end
188
+ end.join
189
+ end
190
+
191
+ def patch_file_mode_changed?(patch)
192
+ patch.delta.old_file[:mode] != patch.delta.new_file[:mode]
193
+ end
194
+
195
+ def patch_file_name(from, to)
196
+ file_name(from, to, 'patch')
197
+ end
198
+
199
+ def file_name(*args, ext)
200
+ "#{[full_name, args].flatten.join('_')}.#{ext}"
201
+ end
202
+
203
+ def create_file(file_path, &blk)
204
+ File.open(file_path, File::RDWR|File::CREAT, &blk)
205
+ end
206
+
207
+ def diff_patches(from, to)
208
+ (@diff_patches ||= {})[[from, to]] = repo.patches(from, to, paths: include_paths_or_cwd, exclude_paths: exclude_paths(true))
121
209
  end
122
210
 
123
211
  def include_paths_or_cwd
@@ -129,14 +217,25 @@ module Dapp
129
217
  end
130
218
  end
131
219
 
132
- def diff_patches(from, to)
133
- repo.diff(from, to, paths: include_paths_or_cwd).patches.select do |p|
134
- exclude_paths(true).any? { |path| !p.delta.new_file[:path].start_with?(path) }
135
- end
220
+ def exclude_paths(with_cwd = false)
221
+ base_paths(repo.exclude_paths + @exclude_paths, with_cwd)
222
+ end
223
+
224
+ def include_paths(with_cwd = false)
225
+ base_paths(@include_paths, with_cwd)
136
226
  end
137
227
 
138
- def file_exist_in_repo?(from, path)
139
- repo.file_exist_in_tree?(repo.lookup_commit(from).tree, path.split('/'))
228
+ def base_paths(paths, with_cwd = false)
229
+ [paths].flatten.compact.map do |path|
230
+ if with_cwd && !cwd.empty?
231
+ File.join(cwd, path)
232
+ else
233
+ path
234
+ end
235
+ .chomp('/')
236
+ .reverse.chomp('/')
237
+ .reverse
238
+ end
140
239
  end
141
240
  end
142
241
  end
@@ -14,58 +14,74 @@ module Dapp
14
14
  []
15
15
  end
16
16
 
17
- def container_path
18
- raise
17
+ def patches(from, to, exclude_paths: [], **kwargs)
18
+ diff(from, to, **kwargs).patches.select do |patch|
19
+ !exclude_paths.any? { |p| check_path?(patch.delta.new_file[:path], p) }
20
+ end
19
21
  end
20
22
 
21
- def path
22
- raise
23
- end
24
-
25
- def git_bare
26
- @git_bare ||= Rugged::Repository.new(path, bare: true)
23
+ def diff(from, to, **kwargs)
24
+ if from.nil?
25
+ Rugged::Tree.diff(git, nil, to, **kwargs)
26
+ else
27
+ lookup_commit(from).diff(lookup_commit(to), **kwargs)
28
+ end
27
29
  end
28
30
 
29
- def diff(from, to, **kwargs)
30
- lookup_commit(from).diff(lookup_commit(to), **kwargs)
31
+ def commit_exists?(commit)
32
+ git.exists?(commit)
31
33
  end
32
34
 
33
- def commit_at(commit)
34
- lookup_commit(commit).time.to_i
35
+ def latest_commit(_branch)
36
+ raise
35
37
  end
36
38
 
37
- def latest_commit(branch)
38
- return git_bare.head.target_id if branch == 'HEAD'
39
- git_bare.branches[branch].target_id
39
+ def branch
40
+ git.head.name.sub(/^refs\/heads\//, '')
40
41
  end
41
42
 
42
- def cleanup!
43
+ def commit_at(commit)
44
+ lookup_commit(commit).time.to_i
43
45
  end
44
46
 
45
- def branch
46
- git_bare.head.name.sub(/^refs\/heads\//, '')
47
+ def find_commit_id_by_message(regex)
48
+ walker.each do |commit|
49
+ next unless commit.message =~ regex
50
+ return commit.oid
51
+ end
47
52
  end
48
53
 
49
- def file_exist_in_tree?(tree, paths)
50
- path = paths.shift
51
- paths.empty? ?
52
- tree.each { |obj| return true if File.fnmatch(path, obj[:name]) } :
53
- tree.each_tree { |tree_obj| return file_exist_in_tree?(lookup_object(tree_obj[:oid]), paths) if File.fnmatch(path, tree_obj[:name]) }
54
- false
54
+ def walker
55
+ walker = Rugged::Walker.new(git)
56
+ walker.push(git.head.target_id)
57
+ walker
55
58
  end
56
59
 
57
60
  def lookup_object(oid)
58
- git_bare.lookup(oid)
61
+ git.lookup(oid)
59
62
  end
60
63
 
61
64
  def lookup_commit(commit)
62
- git_bare.lookup(commit)
65
+ git.lookup(commit)
63
66
  end
64
67
 
65
68
  protected
66
69
 
67
- def git
68
- @git ||= Rugged::Repository.new(path)
70
+ def git(**kwargs)
71
+ @git ||= Rugged::Repository.new(path, **kwargs)
72
+ end
73
+
74
+ private
75
+
76
+ def check_path?(path, format)
77
+ path_parts = path.split('/')
78
+ checking_path = nil
79
+
80
+ until path_parts.empty?
81
+ checking_path = [checking_path, path_parts.shift].compact.join('/')
82
+ return true if File.fnmatch(format, checking_path)
83
+ end
84
+ false
69
85
  end
70
86
  end
71
87
  end