lib/nutkins/docker_builder.rb in nutkins-0.7.0 vs lib/nutkins/docker_builder.rb in nutkins-0.8.0

- old
+ new

@@ -13,108 +13,107 @@ unless Nutkins::Docker.run 'inspect', base, stderr: false puts "getting base image" Docker.run 'pull', base, stdout: true end - cont_id = Nutkins::Docker.container_id_for_tag base, running: true - if cont_id - puts "found existing container #{cont_id}" - Nutkins::Docker.kill_and_remove_container cont_id - puts "killed and removed existing container" - end - # the base image to start rebuilding from parent_img_id = base - cont_id = nil pwd = Dir.pwd begin Dir.chdir cfg["directory"] cache_is_dirty = false build_commands = cfg["build"]["commands"] build_commands.each do |build_cmd| cmd = /^\w+/.match(build_cmd).to_s.downcase cmd_args = build_cmd[(cmd.length + 1)..-1].strip + # docker run is always used and forms the basis of the cache key + run_args = nil + env_args = nil + add_files = nil + add_files_dest = nil - docker_args = [] - # the commit_msg is used to look up cache entries, it can be - # modified if the command uses dynamic data, e.g. to add checksums - commit_msg = nil - case cmd when "run" cmd_args.gsub! /\n+/, ' ' - docker_args = ['exec', '%CONT_ID%', cfg['shell'], '-c', cmd_args] - commit_msg = cmd + ' ' + cmd_args + run_args = cmd_args when "add" - *srcs, dest = cmd_args.split ' ' - srcs = srcs.map { |src| Dir.glob src }.flatten - - docker_args = srcs.map { |src| ['cp', src, '%CONT_ID%' + ':' + dest] } - # ensure checksum of each file is embedded into commit_msg + *add_files, add_files_dest = cmd_args.split ' ' + add_files = add_files.map { |src| Dir.glob src }.flatten + # ensure checksum of each file is embedded into run command # if any file changes the cache is dirtied - commit_msg = 'add ' + srcs.map do |src| + run_args = '#(nop) add ' + add_files.map do |src| src + ':' + Digest::MD5.file(src).to_s - end.push(dest).join(' ') + end.push(add_files_dest).join(' ') when "cmd", "entrypoint", "env", "expose", "label", "onbuild", "user", "volume", "workdir" - commit_msg = commit_change = build_cmd + run_args = "#(nop) #{build_cmd}" + env_args = build_cmd else raise "unsupported command: #{cmd}" # TODO add metadata flags end - if (commit_change or docker_args) and commit_msg - commit_msg = "#{parent_img_id} -> #{commit_msg}" - + if run_args + run_shell_cmd = [ cfg['shell'], '-c', run_args ] unless cache_is_dirty # searches the commit messages of all images for the one matching the expected # cache entry for the given content - all_images = Nutkins::Docker.run_get_stdout('images', '-aq').split("\n") - images_meta = JSON.parse(Nutkins::Docker.run_get_stdout('inspect', *all_images)) - cache_entry = images_meta.find do |image_meta| - if image_meta['Comment'] == commit_msg - parent_img_id = Nutkins::Docker.get_short_commit(image_meta['Id']) - true - end - end + cache_img_id = find_cached_img_id run_shell_cmd - if cache_entry - puts "cached: #{commit_msg}" + if cache_img_id + puts "cached: #{run_args}" + parent_img_id = cache_img_id next else - puts "starting build container from commit #{parent_img_id}" - Nutkins::Docker.run 'run', '-d', parent_img_id, 'sleep', '3600' - cont_id = Nutkins::Docker.container_id_for_tag parent_img_id, running: true - puts "started build container #{cont_id}" + puts "not in cache, starting from #{parent_img_id}" cache_is_dirty = true end end - puts "#{cmd}: #{cmd_args}" + if run_args + puts "run #{run_args}" + unless Nutkins::Docker.run 'run', parent_img_id, *run_shell_cmd, stdout: true + raise "run failed: #{run_args}" + end - unless docker_args.empty? - # docker can be an array of one set of args, or an array of arrays of args - docker_args = [ docker_args ] unless docker_args[0].kind_of? Array - docker_args.each do |one_docker_args| - run_args = one_docker_args.map { |arg| arg.gsub '%CONT_ID%', cont_id } - puts "run #{run_args.join ' '}" - unless Nutkins::Docker.run *run_args, stdout: true - raise "build failed: #{one_docker_args.join ' '}" + cont_id = `docker ps -aq`.lines.first.strip + begin + if add_files + add_files.each do |src| + if not Nutkins::Docker.run 'cp', src, "#{cont_id}:#{add_files_dest}" + raise "could not copy #{src} to #{cont_id}:#{add_files_dest}" + end + end end + + commit_args = env_args ? ['-c', env_args] : [] + parent_img_id = Nutkins::Docker.run_get_stdout 'commit', *commit_args, cont_id + raise "could not commit docker image" if parent_img_id.nil? + parent_img_id = Nutkins::Docker.get_short_commit parent_img_id + ensure + if not Nutkins::Docker.run 'rm', cont_id + puts "could not remove build container #{cont_id}" + end end end - - commit_args = commit_change ? ['-c', commit_change] : [] - parent_img_id = Nutkins::Docker.run_get_stdout 'commit', '-m', commit_msg, *commit_args, cont_id - raise "could not commit docker image" if parent_img_id.nil? - parent_img_id = Nutkins::Docker.get_short_commit parent_img_id else puts "TODO: support cmd #{build_cmd}" end end ensure Dir.chdir pwd - Nutkins::Docker.kill_and_remove_container cont_id if cont_id - puts "killed and removed build container" end + + Nutkins::Docker.run 'tag', parent_img_id, cfg['tag'] + end + + def self.find_cached_img_id command + all_images = Nutkins::Docker.run_get_stdout('images', '-aq').split("\n") + images_meta = JSON.parse(Nutkins::Docker.run_get_stdout('inspect', *all_images)) + images_meta.each do |image_meta| + if image_meta.dig('ContainerConfig', 'Cmd') == command + return Nutkins::Docker.get_short_commit(image_meta['Id']) + end + end + nil end end