8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
# File 'lib/nutkins/docker_builder.rb', line 8
def self.build cfg, base_tag = nil
base = base_tag || cfg["base"]
raise "to use build commands you must specify the base image" unless base
unless Docker.run 'inspect', base, stderr: false
puts "getting base image"
raise "could not find base image #{base}" unless Docker.run 'pull', base, stdout: true
end
parent_img_id = Docker.get_short_commit Docker.container_id_for_name(base)
pwd = Dir.pwd
begin
Dir.chdir cfg["directory"]
cache_is_dirty = false
build_commands = cfg["build"]["commands"]
build_commands.each do |build_cmd|
unless build_cmd.kind_of? Hash or build_cmd.keys.length != 1
raise "build command should be single value hash e.g. 'run:bash'"
end
cmd = build_cmd.keys.first.downcase
cmd_args = build_cmd[cmd]
run_args = nil
env_args = nil
copies = []
case cmd
when "run"
if cmd_args.kind_of? String
run_args = cmd_args.gsub /\n+/, ' '
else
run_args = cmd_args.join ' && '
end
when "copy"
if cmd_args.kind_of? String
all_copy_args = [ cmd_args ]
else
all_copy_args = cmd_args
end
copies = all_copy_args.map do |copy_args|
*add_files, add_files_dest = copy_args.split ' '
add_files = add_files.map { |src| Dir.glob src }.flatten
if not run_args
run_args = '#(nop) copy '
else
run_args += ';'
end
run_args += add_files.map do |src|
if File.directory? src
md5 = Digest::MD5.new
update_md5_dir = Proc.new do |dir|
Dir.glob("#{dir}/*").each do |dir_entry|
if File.directory? dir_entry
update_md5_dir.call dir_entry
else
md5.update(File.read dir_entry)
end
end
end
update_md5_dir.call src
hash = md5.hexdigest
else
hash = Digest::MD5.file(src).to_s
end
src + ':' + hash
end.push(add_files_dest).join(' ')
{ srcs: add_files, dest: add_files_dest }
end
when "cmd", "entrypoint", "env", "expose", "label", "onbuild", "user", "volume", "workdir"
env_args = cmd + ' ' + (cmd_args.kind_of?(String) ? cmd_args : JSON.dump(cmd_args))
run_args = "#(nop) #{env_args}"
else
raise "unsupported command: #{cmd}"
end
if run_args
run_shell_cmd = [ cfg['shell'], '-c', run_args ]
unless cache_is_dirty
cache_img_id = find_cached_img_id parent_img_id, run_shell_cmd
if cache_img_id
puts "cached: #{run_args}"
parent_img_id = cache_img_id
next
else
puts "not in cache: #{run_args} - starting from #{parent_img_id}"
cache_is_dirty = true
end
end
if run_args
puts "run #{run_args}"
unless Docker.run 'run', parent_img_id, *run_shell_cmd, stdout: true
raise "run failed: #{run_args}"
end
cont_id = `docker ps -aq`.lines.first.strip
begin
unless copies.empty?
copies.each do |copy|
copy[:srcs].each do |src|
puts "copy #{src} -> #{cont_id}:#{copy[:dest]}"
if not Docker.run 'cp', src, "#{cont_id}:#{copy[:dest]}"
raise "could not copy #{src} to #{cont_id}:#{copy[:dest]}"
end
end
end
end
commit_args = env_args ? ['-c', env_args] : []
parent_img_id = Docker.run_get_stdout 'commit', *commit_args, cont_id
raise "could not commit docker image" if parent_img_id.nil?
parent_img_id = Docker.get_short_commit parent_img_id
ensure
if not Docker.run 'rm', cont_id
puts "could not remove build container #{cont_id}"
end
end
end
else
puts "TODO: support cmd #{build_cmd}"
end
end
ensure
Dir.chdir pwd
end
Docker.run 'tag', parent_img_id, cfg['tag']
end
|