dapp 0.0.1
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 +15 -0
- data/bin/dapp +8 -0
- data/lib/dapp.rb +21 -0
- data/lib/dapp/atomizer.rb +53 -0
- data/lib/dapp/builder.rb +217 -0
- data/lib/dapp/builder/cascade_tagging.rb +48 -0
- data/lib/dapp/builder/centos7.rb +46 -0
- data/lib/dapp/builder/chefify.rb +101 -0
- data/lib/dapp/cli.rb +133 -0
- data/lib/dapp/docker.rb +188 -0
- data/lib/dapp/filelock.rb +42 -0
- data/lib/dapp/git_artifact.rb +330 -0
- data/lib/dapp/git_repo/base.rb +50 -0
- data/lib/dapp/git_repo/chronicler.rb +43 -0
- data/lib/dapp/git_repo/remote.rb +59 -0
- data/lib/dapp/version.rb +3 -0
- metadata +210 -0
data/lib/dapp/cli.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'mixlib/cli'
|
2
|
+
|
3
|
+
module Dapp
|
4
|
+
# CLI
|
5
|
+
class CLI
|
6
|
+
include Mixlib::CLI
|
7
|
+
|
8
|
+
banner <<BANNER.freeze
|
9
|
+
Version: #{Dapp::VERSION}
|
10
|
+
|
11
|
+
Usage:
|
12
|
+
dappit [options] [PATTERN ...]
|
13
|
+
|
14
|
+
PATTERN Applications to process [default: *].
|
15
|
+
|
16
|
+
Options:
|
17
|
+
BANNER
|
18
|
+
|
19
|
+
option :version,
|
20
|
+
long: '--version',
|
21
|
+
description: 'Show version',
|
22
|
+
on: :tail,
|
23
|
+
boolean: true,
|
24
|
+
proc: proc { puts "Version: #{Dapp::VERSION}" },
|
25
|
+
exit: 0
|
26
|
+
|
27
|
+
option :quiet,
|
28
|
+
short: '-q',
|
29
|
+
long: '--quiet',
|
30
|
+
description: 'Suppress logging',
|
31
|
+
on: :tail,
|
32
|
+
boolean: true,
|
33
|
+
proc: proc { Dapp::Builder.default_opts[:log_quiet] = true }
|
34
|
+
|
35
|
+
option :verbose,
|
36
|
+
long: '--verbose',
|
37
|
+
description: 'Enable verbose output',
|
38
|
+
on: :tail,
|
39
|
+
boolean: true,
|
40
|
+
proc: proc { Dapp::Builder.default_opts[:log_verbose] = true }
|
41
|
+
|
42
|
+
option :help,
|
43
|
+
short: '-h',
|
44
|
+
long: '--help',
|
45
|
+
description: 'Show this message',
|
46
|
+
on: :tail,
|
47
|
+
boolean: true,
|
48
|
+
show_options: true,
|
49
|
+
exit: 0
|
50
|
+
|
51
|
+
option :build_dir,
|
52
|
+
long: '--build-dir PATH',
|
53
|
+
description: 'Build directory',
|
54
|
+
proc: proc { |p| Dapp::Builder.default_opts[:build_dir] = p }
|
55
|
+
|
56
|
+
option :dir,
|
57
|
+
long: '--dir PATH',
|
58
|
+
description: 'Change to directory',
|
59
|
+
on: :head
|
60
|
+
|
61
|
+
option :dappfile_name,
|
62
|
+
long: '--dappfile-name NAME',
|
63
|
+
description: 'Name of Dappfile',
|
64
|
+
proc: proc { |n| Dapp::Builder.default_opts[:dappfile_name] = n },
|
65
|
+
on: :head
|
66
|
+
|
67
|
+
option :flush_cache,
|
68
|
+
long: '--flush-cache',
|
69
|
+
description: 'Flush cache',
|
70
|
+
boolean: true,
|
71
|
+
proc: proc { Dapp::Builder.default_opts[:flush_cache] = true }
|
72
|
+
|
73
|
+
option :docker_registry,
|
74
|
+
long: '--docker-registry REGISTRY',
|
75
|
+
description: 'Docker registry',
|
76
|
+
proc: proc { |r| Dapp::Builder.default_opts[:docker_registry] = r }
|
77
|
+
|
78
|
+
option :cascade_tagging,
|
79
|
+
long: '--cascade_tagging',
|
80
|
+
description: 'Use cascade tagging',
|
81
|
+
boolean: true,
|
82
|
+
proc: proc { Dapp::Builder.default_opts[:cascade_tagging] = true }
|
83
|
+
|
84
|
+
option :git_artifact_branch,
|
85
|
+
long: '--git-artifact-branch BRANCH',
|
86
|
+
description: 'Default branch to archive artifacts from',
|
87
|
+
proc: proc { |b| Dapp::Builder.default_opts[:git_artifact_branch] = b }
|
88
|
+
|
89
|
+
def dappfile_path
|
90
|
+
@dappfile_path ||= File.join [config[:dir], 'Dappfile'].compact
|
91
|
+
end
|
92
|
+
|
93
|
+
def patterns
|
94
|
+
@patterns ||= cli_arguments
|
95
|
+
end
|
96
|
+
|
97
|
+
def run(argv = ARGV)
|
98
|
+
begin
|
99
|
+
parse_options(argv)
|
100
|
+
rescue OptionParser::InvalidOption => e
|
101
|
+
STDERR.puts "Error: #{e.message}"
|
102
|
+
exit 1
|
103
|
+
end
|
104
|
+
|
105
|
+
patterns << '*' unless patterns.any?
|
106
|
+
|
107
|
+
if File.exist? dappfile_path
|
108
|
+
process_file
|
109
|
+
else
|
110
|
+
process_directory
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def process_file
|
115
|
+
patterns.each do |pattern|
|
116
|
+
unless Dapp::Builder.process_file(dappfile_path, app_filter: pattern).builded_apps.any?
|
117
|
+
STDERR.puts "Error! No such app: '#{pattern}' in #{dappfile_path}"
|
118
|
+
exit 1
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def process_directory
|
124
|
+
Dapp::Builder.default_opts[:shared_build_dir] = true
|
125
|
+
patterns.each do |pattern|
|
126
|
+
unless Dapp::Builder.process_directory(config[:dir], pattern).any?
|
127
|
+
STDERR.puts "Error! No such app '#{pattern}'"
|
128
|
+
exit 1
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/lib/dapp/docker.rb
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
module Dapp
|
2
|
+
# Dockerfile builder and docker binary wrapper
|
3
|
+
class Docker
|
4
|
+
def initialize(builder)
|
5
|
+
@builder = builder
|
6
|
+
end
|
7
|
+
|
8
|
+
def name(name = nil)
|
9
|
+
@name = name || @name
|
10
|
+
end
|
11
|
+
|
12
|
+
def from(from = nil)
|
13
|
+
@from = from || @from
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_path(*path)
|
17
|
+
builder.build_path(builder.home_branch, [name, 'docker'].compact.join('.'), *path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def run(*command, step: :build)
|
21
|
+
add_instruction step, :run, command.join(' && ')
|
22
|
+
end
|
23
|
+
|
24
|
+
def workdir(directory, step: :build)
|
25
|
+
add_instruction step, :workdir, directory
|
26
|
+
end
|
27
|
+
|
28
|
+
def copy(what, where, step: :build)
|
29
|
+
add_instruction step, :copy, what, where
|
30
|
+
end
|
31
|
+
|
32
|
+
def add(what, where, step: :build)
|
33
|
+
add_instruction step, :add, what, where
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_artifact(file_path, filename, where, step: :build)
|
37
|
+
add_instruction step, :add_artifact, file_path, filename, where
|
38
|
+
end
|
39
|
+
|
40
|
+
def expose(*ports, step: :build)
|
41
|
+
add_instruction step, :expose, ports
|
42
|
+
end
|
43
|
+
|
44
|
+
def env(step: :build, **env)
|
45
|
+
add_instruction step, :env, env
|
46
|
+
end
|
47
|
+
|
48
|
+
def volume(*paths, step: :build)
|
49
|
+
add_instruction step, :volume, paths
|
50
|
+
end
|
51
|
+
|
52
|
+
def cmd(*commands, step: :build)
|
53
|
+
add_instruction step, :cmd, commands
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize_dup(other)
|
57
|
+
super
|
58
|
+
|
59
|
+
@name = @name.dup if @name
|
60
|
+
@from = @from.dup if @from
|
61
|
+
|
62
|
+
@instructions = @instructions.dup
|
63
|
+
@instructions.each do |step, step_instructions|
|
64
|
+
@instructions[step] = step_instructions.dup
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def build
|
69
|
+
# prepare
|
70
|
+
generate_dockerfile
|
71
|
+
|
72
|
+
# build
|
73
|
+
res = docker("build --force-rm=true --rm=true #{build_path}", log_verbose: true)
|
74
|
+
|
75
|
+
# return image id
|
76
|
+
res.stdout.lines.grep(/^Successfully built ([0-9a-f]+)\n$/).first.strip.split.last
|
77
|
+
end
|
78
|
+
|
79
|
+
def image_exists?(**kwargs)
|
80
|
+
!image_id(**kwargs).nil?
|
81
|
+
end
|
82
|
+
|
83
|
+
def image_id(**kwargs)
|
84
|
+
image = images(**kwargs).first
|
85
|
+
image ? image[:id] : nil
|
86
|
+
end
|
87
|
+
|
88
|
+
def images(name:, tag: nil, registry: nil)
|
89
|
+
all_images.select { |i| i[:name] == pad_image_name(name: name, registry: registry) && (!tag || i[:tag] == tag) }
|
90
|
+
end
|
91
|
+
|
92
|
+
def all_images
|
93
|
+
docker('images').stdout.lines.drop(1).map(&:strip)
|
94
|
+
.map { |line| Hash[[:name, :tag, :id].zip(line.strip.split(/\s{2,}/)[0..3])] }
|
95
|
+
end
|
96
|
+
|
97
|
+
def tag(origin, new, force: true)
|
98
|
+
docker "tag#{' -f' if force} #{origin.is_a?(String) ? origin : pad_image_name(**origin)} #{pad_image_name(**new)}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def rmi(**kwargs)
|
102
|
+
docker "rmi #{pad_image_name(**kwargs)}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def push(name:, tag: nil, registry: nil)
|
106
|
+
docker "push #{pad_image_name name: name, tag: tag, registry: registry}", log_verbose: true
|
107
|
+
end
|
108
|
+
|
109
|
+
protected
|
110
|
+
|
111
|
+
def pad_image_name(name:, tag: nil, registry: nil)
|
112
|
+
name = "#{registry}/#{name}" if registry
|
113
|
+
name = "#{name}:#{tag}" if tag
|
114
|
+
name
|
115
|
+
end
|
116
|
+
|
117
|
+
attr_reader :builder
|
118
|
+
|
119
|
+
def instructions(step)
|
120
|
+
(@instructions ||= {})[step] ||= []
|
121
|
+
end
|
122
|
+
|
123
|
+
def add_instruction(step, *args)
|
124
|
+
instructions(step) << args
|
125
|
+
end
|
126
|
+
|
127
|
+
def docker(command, **kwargs)
|
128
|
+
builder.shellout "docker #{command}", **kwargs
|
129
|
+
end
|
130
|
+
|
131
|
+
def dockerfile_path
|
132
|
+
build_path 'Dockerfile'
|
133
|
+
end
|
134
|
+
|
135
|
+
def generate_dockerfile_run(dockerfile, *params)
|
136
|
+
dockerfile.puts 'RUN ' + params[0]
|
137
|
+
end
|
138
|
+
|
139
|
+
def generate_dockerfile_copy(dockerfile, *params)
|
140
|
+
dockerfile.puts "COPY #{params[0]} #{params[1]}"
|
141
|
+
end
|
142
|
+
|
143
|
+
def generate_dockerfile_add(dockerfile, *params)
|
144
|
+
dockerfile.puts "ADD #{params[0]} #{params[1]}"
|
145
|
+
end
|
146
|
+
|
147
|
+
def generate_dockerfile_add_artifact(dockerfile, *params)
|
148
|
+
FileUtils.link params[0], build_path(params[1]), force: true
|
149
|
+
dockerfile.puts "ADD #{params[1]} #{params[2]}"
|
150
|
+
end
|
151
|
+
|
152
|
+
def generate_dockerfile_expose(dockerfile, *params)
|
153
|
+
dockerfile.puts 'EXPOSE ' + params[0].map(&:to_s).join(' ')
|
154
|
+
end
|
155
|
+
|
156
|
+
def generate_dockerfile_env(dockerfile, *params)
|
157
|
+
dockerfile.puts 'ENV ' + params[0].map { |k, v| %(#{k}="#{v}") }.join(' ')
|
158
|
+
end
|
159
|
+
|
160
|
+
def generate_dockerfile_volume(dockerfile, *params)
|
161
|
+
dockerfile.puts 'VOLUME ' + params[0].join(' ')
|
162
|
+
end
|
163
|
+
|
164
|
+
def generate_dockerfile_workdir(dockerfile, *params)
|
165
|
+
dockerfile.puts "WORKDIR #{params[0]}"
|
166
|
+
end
|
167
|
+
|
168
|
+
def generate_dockerfile_cmd(dockerfile, *params)
|
169
|
+
dockerfile.puts 'CMD ' + params[0].join(' ')
|
170
|
+
end
|
171
|
+
|
172
|
+
def generate_dockerfile_step(dockerfile, step)
|
173
|
+
instructions(step).each do |directive, *params|
|
174
|
+
send :"generate_dockerfile_#{directive}", dockerfile, *params
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def generate_dockerfile
|
179
|
+
File.open dockerfile_path, 'w' do |dockerfile|
|
180
|
+
dockerfile.puts 'FROM ' + from
|
181
|
+
|
182
|
+
[:begining, :prepare, :build, :setup].each do |step|
|
183
|
+
generate_dockerfile_step(dockerfile, step)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Dapp
|
2
|
+
# File Monitor
|
3
|
+
module Filelock
|
4
|
+
def self.included(base)
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
# ClassMethods
|
9
|
+
module ClassMethods
|
10
|
+
def filelocks
|
11
|
+
@filelocks ||= Hash.new(false)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def filelock(filelock, error_message: 'Already in use!', timeout: 10, &blk)
|
16
|
+
return yield if self.class.filelocks[filelock]
|
17
|
+
|
18
|
+
begin
|
19
|
+
self.class.filelocks[filelock] = true
|
20
|
+
filelock_lockfile(filelock, error_message: error_message, timeout: timeout, &blk)
|
21
|
+
yield
|
22
|
+
ensure
|
23
|
+
self.class.filelocks[filelock] = false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def filelock_lockfile(filelock, error_message: 'Already in use!', timeout: 10)
|
30
|
+
File.open(build_path(filelock), File::RDWR | File::CREAT, 0644) do |file|
|
31
|
+
Timeout.timeout(timeout) do
|
32
|
+
file.flock(File::LOCK_EX)
|
33
|
+
end
|
34
|
+
|
35
|
+
yield
|
36
|
+
end
|
37
|
+
rescue Timeout::Error
|
38
|
+
STDERR.puts error_message
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,330 @@
|
|
1
|
+
module Dapp
|
2
|
+
# Artifact from Git repo
|
3
|
+
class GitArtifact
|
4
|
+
# rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
|
5
|
+
def initialize(builder, repo, where_to_add, name: nil, branch: 'master', cwd: nil, paths: nil, owner: nil, group: nil,
|
6
|
+
interlayer_period: 7 * 24 * 3600, build_path: nil, flush_cache: false)
|
7
|
+
@builder = builder
|
8
|
+
@repo = repo
|
9
|
+
@name = name
|
10
|
+
|
11
|
+
@where_to_add = where_to_add
|
12
|
+
@branch = branch
|
13
|
+
@cwd = cwd
|
14
|
+
@paths = paths
|
15
|
+
@owner = owner
|
16
|
+
@group = group
|
17
|
+
|
18
|
+
@interlayer_period = interlayer_period
|
19
|
+
|
20
|
+
@build_path = build_path || []
|
21
|
+
|
22
|
+
@atomizer = Atomizer.new builder, build_path(filename('.atomizer'))
|
23
|
+
|
24
|
+
# check params hash
|
25
|
+
lock do
|
26
|
+
cleanup! unless !flush_cache && File.exist?(paramshash_path) && File.read(paramshash_path) == paramshash
|
27
|
+
File.write paramshash_path, paramshash
|
28
|
+
end
|
29
|
+
end
|
30
|
+
# rubocop:enable Metrics/ParameterLists, Metrics/MethodLength
|
31
|
+
|
32
|
+
def build_path(*paths)
|
33
|
+
builder.build_path(*@build_path, *paths)
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_multilayer!
|
37
|
+
lock_with_repo do
|
38
|
+
# create and add archive
|
39
|
+
create_and_add_archive
|
40
|
+
return if archive_commit == repo_latest_commit
|
41
|
+
|
42
|
+
# add layer patches
|
43
|
+
latest_layer = add_layer_patches
|
44
|
+
if latest_layer
|
45
|
+
latest_layer_commit = layer_commit(latest_layer)
|
46
|
+
return if latest_layer_commit == repo_latest_commit
|
47
|
+
end
|
48
|
+
|
49
|
+
# empty changes
|
50
|
+
unless any_changes?(latest_layer_commit || archive_commit)
|
51
|
+
remove_latest!
|
52
|
+
return
|
53
|
+
end
|
54
|
+
|
55
|
+
# create and add last patch
|
56
|
+
create_and_add_last_patch(latest_layer, latest_layer_commit)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def cleanup!
|
61
|
+
lock do
|
62
|
+
FileUtils.rm_f [
|
63
|
+
paramshash_path,
|
64
|
+
archive_path,
|
65
|
+
archive_commitfile_path,
|
66
|
+
Dir.glob(layer_patch_path('*')),
|
67
|
+
Dir.glob(layer_commitfile_path('*')),
|
68
|
+
latest_patch_path,
|
69
|
+
latest_commitfile_path
|
70
|
+
].flatten
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def exists_in_commit?(path, commit)
|
75
|
+
repo.git_bare("cat-file -e #{commit}:#{path}", returns: [0, 128]).status.success?
|
76
|
+
end
|
77
|
+
|
78
|
+
def exists_in_step?(path, step)
|
79
|
+
exists_in_commit?(path, commit_by_step(step))
|
80
|
+
end
|
81
|
+
|
82
|
+
def prepare_step_commit
|
83
|
+
archive_commit
|
84
|
+
end
|
85
|
+
|
86
|
+
def build_step_commit
|
87
|
+
layer_commit(layers.last) || archive_commit
|
88
|
+
end
|
89
|
+
|
90
|
+
def setup_step_commit
|
91
|
+
latest_commit || layer_commit(layers.last) || archive_commit
|
92
|
+
end
|
93
|
+
|
94
|
+
def commit_by_step(step)
|
95
|
+
send :"#{step}_step_commit"
|
96
|
+
end
|
97
|
+
|
98
|
+
def any_changes?(from)
|
99
|
+
!repo.git_bare("diff --quiet #{from}..#{repo_latest_commit}#{" --relative=#{cwd}" if cwd} #{paths(true)}", returns: [0, 1]).status.success?
|
100
|
+
end
|
101
|
+
|
102
|
+
protected
|
103
|
+
|
104
|
+
attr_reader :builder
|
105
|
+
attr_reader :repo
|
106
|
+
attr_reader :name
|
107
|
+
attr_reader :where_to_add
|
108
|
+
attr_reader :branch
|
109
|
+
attr_reader :cwd
|
110
|
+
attr_reader :owner
|
111
|
+
attr_reader :group
|
112
|
+
attr_reader :interlayer_period
|
113
|
+
attr_reader :atomizer
|
114
|
+
|
115
|
+
def lock_with_repo(&blk)
|
116
|
+
lock do
|
117
|
+
repo.lock(&blk)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def create_and_add_archive
|
122
|
+
create_archive! unless archive_exists?
|
123
|
+
add_archive
|
124
|
+
end
|
125
|
+
|
126
|
+
def add_layer_patches
|
127
|
+
latest_layer = nil
|
128
|
+
layers.each do |layer|
|
129
|
+
add_layer_patch layer
|
130
|
+
latest_layer = layer
|
131
|
+
end
|
132
|
+
|
133
|
+
latest_layer
|
134
|
+
end
|
135
|
+
|
136
|
+
def create_and_add_last_patch_as_layer_patch(latest_layer, latest_layer_commit)
|
137
|
+
remove_latest!
|
138
|
+
layer = latest_layer.to_i + 1
|
139
|
+
create_layer_patch!(latest_layer_commit || archive_commit, layer)
|
140
|
+
add_layer_patch layer
|
141
|
+
end
|
142
|
+
|
143
|
+
def create_and_add_last_patch_as_latest_patch(_latest_layer, latest_layer_commit)
|
144
|
+
if latest_commit != repo_latest_commit
|
145
|
+
create_latest_patch!(latest_layer_commit || archive_commit)
|
146
|
+
end
|
147
|
+
add_latest_patch
|
148
|
+
end
|
149
|
+
|
150
|
+
def create_and_add_last_patch(latest_layer, latest_layer_commit)
|
151
|
+
if (Time.now - repo.commit_at(latest_layer_commit || archive_commit)) > interlayer_period
|
152
|
+
create_and_add_last_patch_as_layer_patch(latest_layer, latest_layer_commit)
|
153
|
+
else
|
154
|
+
create_and_add_last_patch_as_latest_patch(latest_layer, latest_layer_commit)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def paths(with_cwd = false)
|
159
|
+
[@paths].flatten.compact.map { |path| (with_cwd && cwd ? "#{cwd}/#{path}" : path).gsub(%r{^\/*|\/*$}, '') }.join(' ') if @paths
|
160
|
+
end
|
161
|
+
|
162
|
+
def repo_latest_commit
|
163
|
+
repo.latest_commit(branch)
|
164
|
+
end
|
165
|
+
|
166
|
+
def filename(ending)
|
167
|
+
"#{repo.name}#{name ? "_#{name}" : nil}.#{branch}#{ending}"
|
168
|
+
end
|
169
|
+
|
170
|
+
def paramshash_filename
|
171
|
+
filename '.paramshash'
|
172
|
+
end
|
173
|
+
|
174
|
+
def paramshash_path
|
175
|
+
build_path paramshash_filename
|
176
|
+
end
|
177
|
+
|
178
|
+
def paramshash
|
179
|
+
Digest::SHA256.hexdigest [cwd, paths, owner, group].map(&:to_s).join(':::')
|
180
|
+
end
|
181
|
+
|
182
|
+
def archive_filename
|
183
|
+
filename '.tar.gz'
|
184
|
+
end
|
185
|
+
|
186
|
+
def archive_path
|
187
|
+
build_path archive_filename
|
188
|
+
end
|
189
|
+
|
190
|
+
def archive_commitfile_path
|
191
|
+
build_path filename '.commit'
|
192
|
+
end
|
193
|
+
|
194
|
+
def archive_commit
|
195
|
+
File.read archive_commitfile_path
|
196
|
+
end
|
197
|
+
|
198
|
+
def create_arhive_with_owner_substitution!
|
199
|
+
Dir.mktmpdir('change_archive_owner', build_path) do |tmpdir_path|
|
200
|
+
atomizer << tmpdir_path
|
201
|
+
repo.git_bare "archive #{repo_latest_commit}:#{cwd} #{paths} | /bin/tar --extract --directory #{tmpdir_path}"
|
202
|
+
builder.shellout("/usr/bin/find #{tmpdir_path} -maxdepth 1 -mindepth 1 -printf '%P\\n' | /bin/tar -czf #{archive_path} -C #{tmpdir_path}" \
|
203
|
+
" -T - --owner=#{owner || 'root'} --group=#{group || 'root'}")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def create_simple_archive!
|
208
|
+
repo.git_bare "archive --format tar.gz #{repo_latest_commit}:#{cwd} -o #{archive_path} #{paths}"
|
209
|
+
end
|
210
|
+
|
211
|
+
def create_archive!
|
212
|
+
atomizer << archive_path
|
213
|
+
atomizer << archive_commitfile_path
|
214
|
+
|
215
|
+
if owner || group
|
216
|
+
create_arhive_with_owner_substitution!
|
217
|
+
else
|
218
|
+
create_simple_archive!
|
219
|
+
end
|
220
|
+
|
221
|
+
File.write archive_commitfile_path, repo_latest_commit
|
222
|
+
end
|
223
|
+
|
224
|
+
def archive_exists?
|
225
|
+
File.exist? archive_commitfile_path
|
226
|
+
end
|
227
|
+
|
228
|
+
def add_archive
|
229
|
+
builder.docker.add_artifact archive_path, archive_filename, where_to_add, step: :prepare
|
230
|
+
end
|
231
|
+
|
232
|
+
def sudo_format_user(user)
|
233
|
+
user.to_i.to_s == user ? "\\\##{user}" : user
|
234
|
+
end
|
235
|
+
|
236
|
+
def sudo
|
237
|
+
sudo = ''
|
238
|
+
|
239
|
+
if owner || group
|
240
|
+
sudo = 'sudo '
|
241
|
+
sudo += "-u #{sudo_format_user(owner)} " if owner
|
242
|
+
sudo += "-g #{sudo_format_user(group)} " if group
|
243
|
+
end
|
244
|
+
|
245
|
+
sudo
|
246
|
+
end
|
247
|
+
|
248
|
+
def add_patch(filename, step:)
|
249
|
+
builder.docker.add_artifact(build_path(filename), filename, '/tmp', step: step)
|
250
|
+
|
251
|
+
builder.docker.run [
|
252
|
+
"zcat /tmp/#{filename} | #{sudo}git apply --whitespace=nowarn --directory=#{where_to_add}",
|
253
|
+
"rm /tmp/#{filename}"
|
254
|
+
], step: step
|
255
|
+
end
|
256
|
+
|
257
|
+
def create_patch!(from, filename, commitfile_path)
|
258
|
+
atomizer << build_path(filename)
|
259
|
+
atomizer << commitfile_path
|
260
|
+
|
261
|
+
repo.git_bare "diff --binary #{from}..#{repo_latest_commit}#{" --relative=#{cwd}" if cwd} #{paths(true)} | gzip > #{build_path filename}"
|
262
|
+
File.write commitfile_path, repo_latest_commit
|
263
|
+
end
|
264
|
+
|
265
|
+
def layer_filename(layer, ending)
|
266
|
+
filename "_layer_#{layer.is_a?(Fixnum) ? format('%04d', layer) : layer}#{ending}"
|
267
|
+
end
|
268
|
+
|
269
|
+
def layer_patch_filename(layer)
|
270
|
+
layer_filename(layer, '.patch.gz')
|
271
|
+
end
|
272
|
+
|
273
|
+
def layer_patch_path(layer)
|
274
|
+
build_path layer_patch_filename(layer)
|
275
|
+
end
|
276
|
+
|
277
|
+
def layer_commitfile_path(layer)
|
278
|
+
build_path layer_filename(layer, '.commit')
|
279
|
+
end
|
280
|
+
|
281
|
+
def layer_commit(layer)
|
282
|
+
File.read layer_commitfile_path(layer) if File.exist? layer_commitfile_path(layer)
|
283
|
+
end
|
284
|
+
|
285
|
+
def layers
|
286
|
+
Dir.glob(layer_commitfile_path('*')).map { |path| Integer(path.gsub(/.*_(\d+)\.commit$/, '\\1')) }.sort
|
287
|
+
end
|
288
|
+
|
289
|
+
def create_layer_patch!(from, layer)
|
290
|
+
create_patch! from, layer_patch_filename(layer), layer_commitfile_path(layer)
|
291
|
+
end
|
292
|
+
|
293
|
+
def add_layer_patch(layer)
|
294
|
+
add_patch layer_patch_filename(layer), step: :build
|
295
|
+
end
|
296
|
+
|
297
|
+
def latest_patch_filename
|
298
|
+
filename '_latest.patch.gz'
|
299
|
+
end
|
300
|
+
|
301
|
+
def latest_patch_path
|
302
|
+
build_path latest_patch_filename
|
303
|
+
end
|
304
|
+
|
305
|
+
def latest_commitfile_path
|
306
|
+
build_path filename '_latest.commit'
|
307
|
+
end
|
308
|
+
|
309
|
+
def latest_commit
|
310
|
+
File.read latest_commitfile_path if File.exist? latest_commitfile_path
|
311
|
+
end
|
312
|
+
|
313
|
+
def create_latest_patch!(from)
|
314
|
+
create_patch! from, latest_patch_filename, latest_commitfile_path
|
315
|
+
end
|
316
|
+
|
317
|
+
def add_latest_patch
|
318
|
+
add_patch latest_patch_filename, step: :setup
|
319
|
+
end
|
320
|
+
|
321
|
+
def remove_latest!
|
322
|
+
FileUtils.rm_f [latest_patch_path, latest_commitfile_path]
|
323
|
+
end
|
324
|
+
|
325
|
+
def lock(**kwargs, &block)
|
326
|
+
builder.filelock(build_path(filename('.lock')), error_message: "Branch #{branch} of artifact #{name ? " #{name}" : nil} #{repo.name}" \
|
327
|
+
" (#{repo.dir_path}) in use! Try again later.", **kwargs, &block)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|