dockly 2.7.2 → 3.0.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.
data/Gemfile CHANGED
@@ -4,6 +4,3 @@ source 'https://rubygems.org'
4
4
  gem 'foreman', :git => 'https://github.com/adamjt/foreman'
5
5
 
6
6
  gemspec
7
-
8
- gem 'fog-core'
9
- gem 'fog-json'
@@ -18,10 +18,9 @@ Gem::Specification.new do |gem|
18
18
  gem.add_dependency 'docker-api', '>= 1.14.0'
19
19
  gem.add_dependency 'dockly-util', '~> 0.0.9'
20
20
  gem.add_dependency 'excon'
21
- gem.add_dependency 'fog', '~> 1.21'
21
+ gem.add_dependency 'aws-sdk', '~> 2.0'
22
22
  gem.add_dependency 'foreman'
23
23
  gem.add_dependency 'fpm', '~> 1.2.0'
24
- gem.add_dependency 'grit'
25
24
  gem.add_dependency 'rugged'
26
25
  gem.add_development_dependency 'cane'
27
26
  gem.add_development_dependency 'pry'
@@ -1,16 +1,17 @@
1
1
  require 'dockly/util'
2
2
  require 'dockly/util/tar'
3
3
  require 'dockly/util/git'
4
- require 'fog'
5
4
  require 'foreman/cli_fix'
6
5
  require 'foreman/export/base_fix'
7
6
  require 'rugged'
7
+ require 'aws-sdk'
8
8
 
9
9
  module Dockly
10
+ LOAD_FILE = 'dockly.rb'
11
+
10
12
  attr_reader :instance, :git_sha
11
13
  attr_writer :load_file
12
14
 
13
- autoload :AWS, 'dockly/aws'
14
15
  autoload :Foreman, 'dockly/foreman'
15
16
  autoload :BashBuilder, 'dockly/bash_builder'
16
17
  autoload :BuildCache, 'dockly/build_cache'
@@ -18,10 +19,11 @@ module Dockly
18
19
  autoload :Deb, 'dockly/deb'
19
20
  autoload :History, 'dockly/history'
20
21
  autoload :Rpm, 'dockly/rpm'
22
+ autoload :S3Writer, 'dockly/s3_writer'
21
23
  autoload :TarDiff, 'dockly/tar_diff'
22
24
  autoload :VERSION, 'dockly/version'
23
25
 
24
- LOAD_FILE = 'dockly.rb'
26
+ module_function
25
27
 
26
28
  def load_file
27
29
  @load_file || LOAD_FILE
@@ -48,6 +50,41 @@ module Dockly
48
50
  }
49
51
  end
50
52
 
53
+ def git_sha
54
+ @git_sha ||= Dockly::Util::Git.sha
55
+ end
56
+
57
+ def assume_role(role_name = nil)
58
+ @assume_role = role_name if role_name
59
+ @assume_role
60
+ end
61
+
62
+ def perform_role_assumption
63
+ return if assume_role.nil?
64
+ Aws.config.update(
65
+ credentials: Aws::AssumeRoleCredentials.new(
66
+ role_arn: assume_role, role_session_name: 'dockly'
67
+ )
68
+ )
69
+ end
70
+
71
+ def aws_region(region = nil)
72
+ @aws_region = region unless region.nil?
73
+ @aws_region || 'us-east-1'
74
+ end
75
+
76
+ def s3
77
+ @s3 ||= Aws::S3::Client.new(region: aws_region)
78
+ end
79
+
80
+ [:debs, :rpms, :dockers, :foremans].each do |method|
81
+ define_method(method) do
82
+ inst[method]
83
+ end
84
+
85
+ module_function method
86
+ end
87
+
51
88
  {
52
89
  :deb => Dockly::Deb,
53
90
  :rpm => Dockly::Rpm,
@@ -61,21 +98,9 @@ module Dockly
61
98
  klass.new!(:name => sym, &block)
62
99
  end
63
100
  end
64
- end
65
101
 
66
- [:debs, :rpms, :dockers, :foremans].each do |method|
67
- define_method(method) do
68
- inst[method]
69
- end
102
+ module_function method
70
103
  end
71
-
72
- def git_sha
73
- @git_sha ||= Dockly::Util::Git.git_sha
74
- end
75
-
76
- module_function :inst, :load_inst, :setup, :load_file, :load_file=,
77
- :deb, :rpm, :docker, :foreman, :git_sha,
78
- :debs, :rpms, :dockers, :foremans
79
104
  end
80
105
 
81
106
  require 'dockly/rake_task'
@@ -48,9 +48,9 @@ class Dockly::BuildCache::Base
48
48
 
49
49
  def up_to_date?
50
50
  ensure_present! :s3_bucket, :s3_object_prefix
51
- connection.head_object(s3_bucket, s3_object(hash_output))
51
+ connection.head_object(bucket: s3_bucket, key: s3_object(hash_output))
52
52
  true
53
- rescue Excon::Errors::NotFound
53
+ rescue Aws::S3::Errors::NotFound, Aws::S3::Errors::NoSuchKey
54
54
  false
55
55
  end
56
56
 
@@ -63,17 +63,17 @@ class Dockly::BuildCache::Base
63
63
  FileUtils.mkdir_p(File.dirname(file_path))
64
64
  unless File.exist?(file_path)
65
65
  debug 'Pulling build cache from s3'
66
- object = connection.get_object(s3_bucket, file_name)
66
+ object = connection.get_object(bucket: s3_bucket, key: file_name)
67
67
  debug 'Pulled build cache from s3'
68
68
 
69
69
  file = File.open(file_path, 'w+b')
70
- file.write(object.body)
70
+ file.write(object.body.read)
71
71
  file.tap(&:rewind)
72
72
  else
73
73
  info 'Build cache already exists locally'
74
74
  File.open(file_path, 'rb')
75
75
  end
76
- rescue Excon::Errors::NotFound
76
+ rescue Aws::S3::Errors::NotFound, Aws::S3::Errors::NoSuchKey
77
77
  nil
78
78
  end
79
79
 
@@ -89,8 +89,12 @@ class Dockly::BuildCache::Base
89
89
 
90
90
  def push_to_s3(file)
91
91
  ensure_present! :s3_bucket, :s3_object_prefix
92
- connection.put_object(s3_bucket, s3_object(hash_output), file.read)
93
- connection.copy_object(s3_bucket, s3_object(hash_output), s3_bucket, s3_object("latest"))
92
+ connection.put_object(bucket: s3_bucket, key: s3_object(hash_output), body: file)
93
+ connection.copy_object(
94
+ copy_source: [s3_bucket, s3_object(hash_output)].join('/'),
95
+ bucket: s3_bucket,
96
+ key: s3_object('latest')
97
+ )
94
98
  end
95
99
 
96
100
  def file_output(file)
@@ -118,6 +122,6 @@ class Dockly::BuildCache::Base
118
122
  end
119
123
 
120
124
  def connection
121
- Dockly::AWS.s3
125
+ Dockly.s3
122
126
  end
123
127
  end
@@ -52,7 +52,11 @@ class Dockly::Deb
52
52
  ensure_present! :s3_bucket
53
53
  object = s3_object_name_for(sha)
54
54
  info "Copying s3://#{s3_bucket}/#{object} to s3://#{s3_bucket}/#{s3_object_name}"
55
- Dockly::AWS.s3.copy_object(s3_bucket, object, s3_bucket, s3_object_name)
55
+ Dockly.s3.copy_object(
56
+ copy_source: File.join(s3_bucket, object),
57
+ bucket: s3_bucket,
58
+ key: s3_object_name
59
+ )
56
60
  info "Successfully copied s3://#{s3_bucket}/#{object} to s3://#{s3_bucket}/#{s3_object_name}"
57
61
  end
58
62
 
@@ -63,7 +67,7 @@ class Dockly::Deb
63
67
 
64
68
  def exists?
65
69
  debug "#{name}: checking for package: #{s3_url}"
66
- Dockly::AWS.s3.head_object(s3_bucket, s3_object_name)
70
+ Dockly.s3.head_object(bucket: s3_bucket, key: s3_object_name)
67
71
  info "#{name}: found package: #{s3_url}"
68
72
  true
69
73
  rescue
@@ -76,8 +80,9 @@ class Dockly::Deb
76
80
  return if s3_bucket.nil?
77
81
  raise "Package wasn't created!" unless File.exist?(build_path)
78
82
  info "uploading package to s3"
79
- Dockly::AWS.s3.put_bucket(s3_bucket) rescue nil
80
- Dockly::AWS.s3.put_object(s3_bucket, s3_object_name, File.new(build_path))
83
+ File.open(build_path, 'rb') do |file|
84
+ Dockly.s3.put_object(bucket: s3_bucket, key: s3_object_name, body: file)
85
+ end
81
86
  end
82
87
 
83
88
  def s3_url
@@ -85,7 +90,7 @@ class Dockly::Deb
85
90
  end
86
91
 
87
92
  def s3_object_name
88
- s3_object_name_for(Dockly::Util::Git.git_sha)
93
+ s3_object_name_for(Dockly::Util::Git.sha)
89
94
  end
90
95
 
91
96
  def s3_object_name_for(sha)
@@ -36,7 +36,11 @@ class Dockly::Docker
36
36
  return if s3_bucket.nil?
37
37
  object = s3_object_for(sha)
38
38
  info "Copying s3://#{s3_bucket}/#{object} to #{s3_bucket}/#{s3_object}"
39
- Dockly::AWS.s3.copy_object(s3_bucket, object, s3_bucket, s3_object)
39
+ Dockly.s3.copy_object(
40
+ copy_source: File.join(s3_bucket, object),
41
+ bucket: s3_bucket,
42
+ key: s3_object
43
+ )
40
44
  info "Successfully copied s3://#{s3_bucket}/#{object} to s3://#{s3_bucket}/#{s3_object}"
41
45
  end
42
46
 
@@ -147,11 +151,11 @@ class Dockly::Docker
147
151
 
148
152
  FileUtils.rm_rf(git_archive_dir)
149
153
  FileUtils.mkdir_p(git_archive_dir)
150
- info "archiving #{Dockly::Util::Git.git_sha}"
151
- Grit::Git.with_timeout(120) do
152
- Dockly::Util::Git.git_repo.archive_to_file(Dockly::Util::Git.git_sha, prefix, git_archive_path, 'tar', 'cat')
154
+ info "archiving #{Dockly::Util::Git.sha}"
155
+ File.open(git_archive_path, 'wb') do |file|
156
+ Dockly::Util::Git.archive(Dockly::Util::Git.sha, prefix, file)
153
157
  end
154
- info "made the git archive for sha #{Dockly::Util::Git.git_sha}"
158
+ info "made the git archive for sha #{Dockly::Util::Git.sha}"
155
159
  git_archive_path
156
160
  end
157
161
 
@@ -240,7 +244,7 @@ class Dockly::Docker
240
244
  if s3_bucket.nil?
241
245
  output = File.open(tar_path, 'wb')
242
246
  else
243
- output = Dockly::AWS::S3Writer.new(connection, s3_bucket, s3_object)
247
+ output = Dockly::S3Writer.new(Dockly.s3, s3_bucket, s3_object)
244
248
  end
245
249
 
246
250
  gzip_output = Zlib::GzipWriter.new(output)
@@ -302,7 +306,7 @@ class Dockly::Docker
302
306
  end
303
307
 
304
308
  def s3_object
305
- s3_object_for(Dockly::Util::Git.git_sha)
309
+ s3_object_for(Dockly::Util::Git.sha)
306
310
  end
307
311
 
308
312
  def s3_object_for(sha)
@@ -330,9 +334,9 @@ class Dockly::Docker
330
334
  debug "fetching #{import}"
331
335
  File.open("#{path}.tmp", 'wb') do |file|
332
336
  case import
333
- when /^s3:\/\/(?<bucket_name>.+?)\/(?<object_path>.+)$/
334
- connection.get_object(Regexp.last_match[:bucket_name],
335
- Regexp.last_match[:object_path]) do |chunk, remaining, total|
337
+ when /^s3:\/\/(?<bucket>.+?)\/(?<key>.+)$/
338
+ bucket, key = Regexp.last_match[:bucket], Regexp.last_match[:key]
339
+ Dockly.s3.get_object(bucket: bucket, key: key) do |chunk|
336
340
  file.write(chunk)
337
341
  end
338
342
  when /^https?:\/\//
@@ -351,21 +355,15 @@ class Dockly::Docker
351
355
  def exists?
352
356
  return false unless s3_bucket
353
357
  debug "#{name}: checking for package: #{s3_url}"
354
- Dockly::AWS.s3.head_object(s3_bucket, s3_object)
358
+ Dockly.s3.head_object(bucket: s3_bucket, key: s3_object)
355
359
  info "#{name}: found package: #{s3_url}"
356
360
  true
357
- rescue
358
- info "#{name}: could not find package: " +
359
- "#{s3_url}"
361
+ rescue Aws::S3::Errors::NotFound, Aws::S3::Errors::NoSuchKey
362
+ info "#{name}: could not find package: #{s3_url}"
360
363
  false
361
364
  end
362
365
 
363
366
  def repository(value = nil)
364
367
  name(value)
365
368
  end
366
-
367
- private
368
- def connection
369
- Dockly::AWS.s3
370
- end
371
369
  end
@@ -1,172 +1,167 @@
1
1
  require 'rake'
2
2
  require 'dockly'
3
3
 
4
- class Rake::DebTask < Rake::Task
5
- def needed?
6
- raise "Package does not exist" if package.nil?
7
- !!ENV['FORCE'] || !package.exists?
8
- end
4
+ module Dockly::RakeHelper
5
+ module_function
9
6
 
10
- def package
11
- Dockly::Deb[name.split(':').last.to_sym]
7
+ def find_deb!(name)
8
+ deb = Dockly.debs[name]
9
+ raise "No deb named #{name}" if deb.nil?
10
+ deb
12
11
  end
13
- end
14
12
 
15
- class Rake::RpmTask < Rake::Task
16
- def needed?
17
- raise "Package does not exist" if package.nil?
18
- !!ENV['FORCE'] || !package.exists?
13
+ def find_docker!(name)
14
+ docker = Dockly.dockers[name]
15
+ raise "No docker named #{name}" if docker.nil?
16
+ docker
19
17
  end
20
18
 
21
- def package
22
- Dockly::Rpm[name.split(':').last.to_sym]
19
+ def find_rpm!(name)
20
+ rpm = Dockly.rpms[name]
21
+ raise "No rpm named #{name}" if rpm.nil?
22
+ rpm
23
23
  end
24
24
  end
25
25
 
26
- class Rake::DockerTask < Rake::Task
27
- def needed?
28
- raise "Docker does not exist" if docker.nil?
29
- !docker.exists?
26
+ namespace :dockly do
27
+ # Dockly needs to be able to perform HTTP requests, and unfortunately for us
28
+ # WebMock is enabled by default.
29
+ task :enable_http do
30
+ if defined?(WebMock::HttpLibAdapters::NetHttpAdapter)
31
+ WebMock::HttpLibAdapters::NetHttpAdapter.disable!
32
+ end
30
33
  end
31
34
 
32
- def docker
33
- Dockly::Docker[name.split(':').last.to_sym]
35
+ task :load do
36
+ raise "No #{Dockly.load_file} found!" unless File.exist?(Dockly.load_file)
37
+ Dockly.inst
34
38
  end
35
- end
36
39
 
37
- module Rake::DSL
38
- def deb(*args, &block)
39
- Rake::DebTask.define_task(*args, &block)
40
+ task :assume_role => 'dockly:load' do
41
+ Dockly.perform_role_assumption
40
42
  end
41
43
 
42
- def rpm(*args, &block)
43
- Rake::RpmTask.define_task(*args, &block)
44
+ task :init => ['dockly:enable_http', 'dockly:load', 'dockly:assume_role']
45
+
46
+ namespace :deb do
47
+ task :prepare, [:name] => 'dockly:init' do |t, args|
48
+ Dockly::RakeHelper.find_deb!(args[:name]).create_package!
49
+ end
50
+
51
+ task :upload, [:name] => 'dockly:init' do |t, args|
52
+ Dockly::RakeHelper.find_deb!(args[:name]).upload_to_s3
53
+ end
54
+
55
+ task :copy, [:name] => 'dockly:init' do |t, args|
56
+ Dockly::RakeHelper
57
+ .find_deb!(args[:name])
58
+ .copy_from_s3(Dockly::History.duplicate_build_sha[0..6])
59
+ end
60
+
61
+ task :build, [:name] => 'dockly:init' do |t, args|
62
+ deb = Dockly::RakeHelper.find_deb!(args[:name])
63
+ deb.build unless deb.exists?
64
+ end
44
65
  end
45
66
 
46
- def docker(*args, &block)
47
- Rake::DockerTask.define_task(*args, &block)
67
+ namespace :rpm do
68
+ task :prepare, [:name] => 'dockly:init' do |t, args|
69
+ Dockly::RakeHelper.find_rpm!(args[:name]).create_package!
70
+ end
71
+
72
+ task :upload, [:name] => 'dockly:init' do |t, args|
73
+ Dockly::RakeHelper.find_rpm!(args[:name]).upload_to_s3
74
+ end
75
+
76
+ task :copy, [:name] => 'dockly:init' do |t, args|
77
+ Dockly::RakeHelper
78
+ .find_rpm!(args[:name])
79
+ .copy_from_s3(Dockly::History.duplicate_build_sha[0..6])
80
+ end
81
+
82
+ task :build, [:name] => 'dockly:init' do |t, args|
83
+ rpm = Dockly::RakeHelper.find_rpm!(args[:name])
84
+ rpm.build unless rpm.exists?
85
+ end
48
86
  end
49
- end
50
87
 
51
- namespace :dockly do
52
- task :load do
53
- raise "No dockly.rb found!" unless File.exist?('dockly.rb')
88
+ namespace :docker do
89
+ task :prepare, [:name] => 'dockly:init' do |t, args|
90
+ Dockly::RakeHelper.find_docker!(args[:name]).generate_build
91
+ end
92
+
93
+ task :upload, [:name] => 'dockly:init' do |t, args|
94
+ docker = Dockly::RakeHelper.find_docker!(args[:name])
95
+ docker.export_only unless docker.exists?
96
+ end
97
+
98
+ task :copy, [:name] => 'dockly:init' do |t, args|
99
+ Dockly::RakeHelper
100
+ .find_docker!(args[:name])
101
+ .copy_from_s3(Dockly::History.duplicate_build_sha[0..6])
102
+ end
103
+
104
+ task :build, [:name] => 'dockly:init' do |t, args|
105
+ docker = Dockly::RakeHelper.find_docker!(args[:name])
106
+ docker.generate! unless docker.exists?
107
+ end
54
108
  end
55
109
 
56
- prepare_targets = []
57
- upload_targets = []
58
- build_targets = []
59
- copy_targets = []
110
+ task :prepare_all => 'dockly:init' do
111
+ Dockly.debs.keys.each do |deb|
112
+ Rake::Task['dockly:deb:prepare'].tap(&:reenable).invoke(deb)
113
+ end
60
114
 
61
- namespace :deb do
62
- Dockly.debs.values.each do |inst|
63
- namespace :prepare do
64
- task inst.name => 'dockly:load' do |name|
65
- inst.create_package!
66
- end
67
- end
68
-
69
- namespace :upload do
70
- deb inst.name => 'dockly:load' do |name|
71
- inst.upload_to_s3
72
- end
73
- end
74
-
75
- namespace :copy do
76
- task inst.name => 'dockly:load' do |name|
77
- inst.copy_from_s3(Dockly::History.duplicate_build_sha[0..6])
78
- end
79
- end
80
-
81
- deb inst.name => 'dockly:load' do |name|
82
- inst.build
83
- end
84
-
85
- prepare_targets << "dockly:deb:prepare:#{inst.name}"
86
- upload_targets << "dockly:deb:upload:#{inst.name}"
87
- copy_targets << "dockly:deb:copy:#{inst.name}"
88
- build_targets << "dockly:deb:#{inst.name}"
115
+ Dockly.rpms.values.each do |rpm|
116
+ Rake::Task['dockly:rpm:prepare'].tap(&:reenable).invoke(rpm)
117
+ end
118
+
119
+ Dockly.dockers.values.each do |docker|
120
+ Rake::Task['dockly:docker:prepare'].tap(&:reenable).invoke(docker)
89
121
  end
90
122
  end
91
123
 
92
- namespace :rpm do
93
- Dockly.rpms.values.each do |inst|
94
- namespace :prepare do
95
- task inst.name => 'dockly:load' do |name|
96
- inst.create_package!
97
- end
98
- end
99
-
100
- namespace :upload do
101
- rpm inst.name => 'dockly:load' do |name|
102
- inst.upload_to_s3
103
- end
104
- end
105
-
106
- namespace :copy do
107
- task inst.name => 'dockly:load' do |name|
108
- inst.copy_from_s3(Dockly::History.duplicate_build_sha[0..6])
109
- end
110
- end
111
-
112
- rpm inst.name => 'dockly:load' do |name|
113
- inst.build
114
- end
115
-
116
- prepare_targets << "dockly:rpm:prepare:#{inst.name}"
117
- upload_targets << "dockly:rpm:upload:#{inst.name}"
118
- copy_targets << "dockly:rpm:copy:#{inst.name}"
119
- build_targets << "dockly:rpm:#{inst.name}"
124
+ task :upload_all => 'dockly:init' do
125
+ Dockly.debs.keys.each do |deb|
126
+ Rake::Task['dockly:deb:upload'].tap(&:reenable).invoke(deb)
127
+ end
128
+
129
+ Dockly.rpms.keys.each do |rpm|
130
+ Rake::Task['dockly:rpm:upload'].tap(&:reenable).invoke(rpm)
131
+ end
132
+
133
+ Dockly.dockers.keys.each do |docker|
134
+ Rake::Task['dockly:docker:upload'].tap(&:reenable).invoke(docker)
120
135
  end
121
136
  end
122
137
 
123
- namespace :docker do
124
- Dockly.dockers.values.each do |inst|
125
- # For backwards compatibility
126
- namespace :noexport do
127
- task inst.name => "dockly:docker:prepare:#{inst.name}"
128
- end
129
-
130
- namespace :prepare do
131
- task inst.name => 'dockly:load' do
132
- Thread.current[:rake_task] = inst.name
133
- inst.generate_build
134
- end
135
- end
136
-
137
- namespace :upload do
138
- docker inst.name => 'dockly:load' do
139
- Thread.current[:rake_task] = inst.name
140
- inst.export_only
141
- end
142
- end
143
-
144
- namespace :copy do
145
- task inst.name => 'dockly:load' do
146
- Thread.current[:rake_task] = inst.name
147
- inst.copy_from_s3(Dockly::History.duplicate_build_sha[0..6])
148
- end
149
- end
150
-
151
- docker inst.name => 'dockly:load' do
152
- Thread.current[:rake_task] = inst.name
153
- inst.generate!
154
- end
155
-
156
- # Docker image will be generated by 'dockly:deb:package'
157
- unless inst.s3_bucket.nil?
158
- prepare_targets << "dockly:docker:prepare:#{inst.name}"
159
- upload_targets << "dockly:docker:upload:#{inst.name}"
160
- copy_targets << "dockly:docker:copy:#{inst.name}"
161
- build_targets << "dockly:docker:#{inst.name}"
162
- end
138
+ task :build_all => 'dockly:init' do
139
+ Dockly.debs.keys.each do |deb|
140
+ Rake::Task['dockly:deb:build'].tap(&:reenable).invoke(deb)
141
+ end
142
+
143
+ Dockly.rpms.keys.each do |rpm|
144
+ Rake::Task['dockly:rpm:build'].tap(&:reenable).invoke(rpm)
145
+ end
146
+
147
+ Dockly.dockers.keys.each do |docker|
148
+ Rake::Task['dockly:docker:build'].tap(&:reenable).invoke(docker)
163
149
  end
164
150
  end
165
151
 
166
- multitask :prepare_all => prepare_targets
167
- multitask :upload_all => upload_targets
168
- multitask :build_all => build_targets
169
- multitask :copy_all => copy_targets
152
+ task :copy_all => 'dockly:init' do
153
+ Dockly.debs.keys.each do |deb|
154
+ Rake::Task['dockly:deb:copy'].tap(&:reenable).invoke(deb)
155
+ end
156
+
157
+ Dockly.rpms.keys.each do |rpm|
158
+ Rake::Task['dockly:rpm:copy'].tap(&:reenable).invoke(rpm)
159
+ end
160
+
161
+ Dockly.dockers.keys.each do |docker|
162
+ Rake::Task['dockly:docker:copy'].tap(&:reenable).invoke(docker)
163
+ end
164
+ end
170
165
 
171
166
  task :build_or_copy_all do
172
167
  if Dockly::History.duplicate_build?