buildizer 0.0.6 → 0.0.7

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.
@@ -9,10 +9,9 @@ module Buildizer
9
9
  Target::Native
10
10
  end
11
11
 
12
- def initial_target_params
13
- super.tap do |params|
14
- params[:package_version] = packager.package_version_tag
15
- end
12
+ def check_params!(params)
13
+ super
14
+ _required_params! :package_version, params
16
15
  end
17
16
 
18
17
  def build_instructions(target)
@@ -0,0 +1,40 @@
1
+ module Buildizer
2
+ module Builder
3
+ class Patch < Base
4
+ def build_type
5
+ 'patch'
6
+ end
7
+
8
+ def target_klass
9
+ Target::Patch
10
+ end
11
+
12
+ def initial_target_params
13
+ super.tap do |params|
14
+ params[:patch] = Array(packager.buildizer_conf['patch'])
15
+ params[:patch_version] = packager.buildizer_conf['patch_version']
16
+ end
17
+ end
18
+
19
+ def do_merge_params(into, params)
20
+ super.tap do |res|
21
+ res[:patch] = (into[:patch] + Array(params['patch'])).uniq
22
+ res[:patch_version] = into[:patch_version] || params['patch_version']
23
+ end
24
+ end
25
+
26
+ def check_params!(params)
27
+ super
28
+ _required_params! :patch_version, params
29
+ end
30
+
31
+ def build_instructions(target)
32
+ target.image.patch_build_instructions(self, target)
33
+ end
34
+
35
+ def build_dep(target)
36
+ target.image.patch_build_dep(self, target)
37
+ end
38
+ end # Patch
39
+ end # Builder
40
+ end # Buildizer
data/lib/buildizer/cli.rb CHANGED
@@ -54,5 +54,11 @@ module Buildizer
54
54
  def deploy
55
55
  self.class.construct_packager(options).deploy!
56
56
  end
57
+
58
+ desc "verify", "Verify targets params"
59
+ shared_options
60
+ def verify
61
+ self.class.construct_packager(options).verify!
62
+ end
57
63
  end # Cli
58
64
  end # Buildizer
@@ -1,17 +1,11 @@
1
1
  module Buildizer
2
2
  class Docker
3
3
  attr_reader :builder
4
- attr_reader :username
5
- attr_reader :password
6
- attr_reader :email
7
- attr_reader :server
4
+ attr_reader :cache
8
5
 
9
- def initialize(builder, username:, password:, email:, server: nil)
6
+ def initialize(builder, cache: nil)
10
7
  @builder = builder
11
- @username = username
12
- @password = password
13
- @email = email
14
- @server = server
8
+ @cache = cache
15
9
  end
16
10
 
17
11
  def image_klass(os_name, os_version)
@@ -19,6 +13,7 @@ module Buildizer
19
13
  'ubuntu' => {
20
14
  '12.04' => Image::Ubuntu1204,
21
15
  '14.04' => Image::Ubuntu1404,
16
+ '16.04' => Image::Ubuntu1604,
22
17
  nil => Image::Ubuntu1404,
23
18
  },
24
19
  'centos' => {
@@ -35,61 +30,127 @@ module Buildizer
35
30
  klass.new(self, **kwargs)
36
31
  end
37
32
 
38
- def login!
39
- docker_login = ["docker login --email=#{email} --username=#{username} --password=#{password}"]
40
- docker_login << "--server=#{server}" if server
41
- builder.packager.command! docker_login.join(' '), desc: "Docker login"
33
+ def with_cache(&blk)
34
+ warn("No docker cache account settings " +
35
+ "(BUILDIZER_DOCKER_CACHE, BUILDIZER_DOCKER_CACHE_USERNAME, " +
36
+ "BUILDIZER_DOCKER_CACHE_PASSWORD, BUILDIZER_DOCKER_CACHE_EMAIL, " +
37
+ "BUILDIZER_DOCKER_CACHE_SERVER) [WARN]") unless cache
38
+
39
+ cache_login! if cache
40
+ begin
41
+ yield if block_given?
42
+ ensure
43
+ cache_logout! if cache
44
+ end
42
45
  end
43
46
 
44
- def logout!
45
- builder.packager.command! 'docker logout', desc: "Docker logout"
46
- end
47
+ def cache_login!
48
+ raise Error, error: :logical_error, message: "no docker cache account info" unless cache
47
49
 
48
- def pull_image!(image)
49
- builder.packager.command "docker pull #{image.base_image}", desc: "Docker pull #{image.base_image}"
50
- builder.packager.command "docker pull #{image.name}", desc: "Docker pull #{image.name}"
50
+ cmd = ["docker login"]
51
+ cmd << "--email=#{cache[:email]}" if cache[:email]
52
+ cmd << "--username=#{cache[:username]}" if cache[:username]
53
+ cmd << "--password=#{cache[:password]}" if cache[:password]
54
+ cmd << "--server=#{cache[:server]}" if cache[:server]
55
+ builder.packager.command! cmd.join(' '), desc: "Docker cache account login"
51
56
  end
52
57
 
53
- def push_image!(image)
54
- builder.packager.command! "docker push #{image.name}", desc: "Docker push #{image.name}"
58
+ def cache_logout!
59
+ raise Error, error: :logical_error, message: "no docker cache account info" unless cache
60
+ builder.packager.command! 'docker logout', desc: "Docker cache account logout"
55
61
  end
56
62
 
57
- def build_image!(image)
58
- pull_image! image
59
-
60
- image_build_path(image).join('Dockerfile').write [*image.instructions, nil].join("\n")
61
- builder.packager.command! "docker build -t #{image.name} #{image_build_path(image)}", desc: "Docker build image #{image.name}"
63
+ def pull_image(image)
64
+ builder.packager.command!("docker pull #{image.base_image}",
65
+ desc: "Docker pull #{image.base_image}")
62
66
 
63
- push_image! image
67
+ builder.packager.command("docker pull #{image.name}",
68
+ desc: "Docker pull #{image.name}") if cache
64
69
  end
65
70
 
66
- def image_build_path(image)
67
- builder.build_path.join(image.os_name).join(image.os_version)
71
+ def push_image(image)
72
+ builder.packager.command("docker push #{image.name}",
73
+ desc: "Docker push #{image.name}") if cache
68
74
  end
69
75
 
70
- def image_runtime_build_path(image)
71
- image_build_path(image).join('build')
76
+ def build_image!(target)
77
+ pull_image target.image
78
+
79
+ target.image_work_path.join('Dockerfile').write [*target.image.instructions, nil].join("\n")
80
+ builder.packager.command! "docker build -t #{target.image.name} #{target.image_work_path}",
81
+ desc: "Docker build image #{target.image.name}"
82
+
83
+ push_image target.image
72
84
  end
73
85
 
74
86
  def container_package_path
75
87
  Pathname.new('/package')
76
88
  end
77
89
 
90
+ def container_package_archive_path
91
+ Pathname.new('/package.tar.gz')
92
+ end
93
+
94
+ def container_package_mount_path
95
+ Pathname.new('/.package')
96
+ end
97
+
78
98
  def container_build_path
79
- container_package_path.join('build')
99
+ Pathname.new('/build')
80
100
  end
81
101
 
82
- def run!(image, cmd:, env: {})
83
- cmd = Array(cmd)
102
+ def container_extra_path
103
+ Pathname.new('/extra')
104
+ end
84
105
 
106
+ def run_target_container!(target:, env: {})
107
+ container = SecureRandom.uuid
108
+ builder.packager.command! [
109
+ "docker run --detach --name #{container}",
110
+ *Array(_common_docker_params(target, env)),
111
+ _wrap_docker_run("while true ; do sleep 1 ; done"),
112
+ ].join(' '), desc: "Run container '#{container}' from docker image '#{target.image.name}'"
113
+ container
114
+ end
115
+
116
+ def shutdown_container!(container:)
117
+ builder.packager.command! "docker kill #{container}", desc: "Kill container '#{container}'"
118
+ builder.packager.command! "docker rm #{container}", desc: "Remove container '#{container}'"
119
+ end
120
+
121
+ def run_in_container!(container:, cmd:, desc: nil)
122
+ builder.packager.command! [
123
+ "docker exec #{container}",
124
+ _wrap_docker_exec(cmd),
125
+ ].join(' '), timeout: 24*60*60, desc: desc
126
+ end
127
+
128
+ def run_in_image!(target:, cmd:, env: {}, desc: nil)
85
129
  builder.packager.command! [
86
130
  "docker run --rm",
87
- *env.map {|k,v| "-e #{k}=#{v}"},
88
- "-v #{builder.packager.package_path}:#{container_package_path}",
89
- "-v #{image_runtime_build_path(image)}:#{container_build_path}",
90
- image.name,
91
- "'#{cmd.join('; ')}'"
92
- ].join(' '), desc: "Run build in docker image #{image.name}"
131
+ *Array(_common_docker_params(target, env)),
132
+ _wrap_docker_run(cmd),
133
+ ].join(' '), timeout: 24*60*60, desc: desc
134
+ end
135
+
136
+ def _common_docker_params(target, env)
137
+ [*env.map {|k,v| "-e #{k}=#{v}"},
138
+ "-v #{builder.packager.package_path}:#{container_package_mount_path}:ro",
139
+ "-v #{target.image_extra_path}:#{container_extra_path}:ro",
140
+ "-v #{target.image_build_path}:#{container_build_path}",
141
+ target.image.name]
142
+ end
143
+
144
+ def _wrap_docker_exec(cmd)
145
+ "/bin/bash -lec '#{_make_cmd(cmd)}'"
146
+ end
147
+
148
+ def _wrap_docker_run(cmd)
149
+ "'#{['set -e', _make_cmd(cmd)].join('; ')}'"
150
+ end
151
+
152
+ def _make_cmd(cmd)
153
+ Array(cmd).join('; ')
93
154
  end
94
155
  end # Docker
95
156
  end # Buildizer
@@ -52,9 +52,21 @@ module Buildizer
52
52
  instructions << [instruction.to_s.upcase, cmd].join(' ')
53
53
  end
54
54
 
55
+ def patch_build_dep(builder, target)
56
+ target_package_spec(target)
57
+ end
58
+
55
59
  def native_build_instructions(builder, target)
56
60
  raise
57
61
  end
62
+
63
+ def patch_build_instructions(builder, target)
64
+ raise
65
+ end
66
+
67
+ def target_package_spec(target)
68
+ raise
69
+ end
58
70
  end # Base
59
71
  end # Image
60
72
  end # Buildizer
@@ -48,23 +48,55 @@ module Buildizer
48
48
  instruction :RUN, "bash -lec \"echo -e '#{repo}' >> /etc/yum.repos.d/CentOS-Extra-Buildizer.repo\""
49
49
  end
50
50
 
51
+ def target_spec_name(target)
52
+ "#{target.base_package_name}.spec"
53
+ end
54
+
55
+ def rpmdev_setuptree_instructions(builder, target)
56
+ "rpmdev-setuptree"
57
+ end
58
+
59
+ def build_rpm_instructions(builder, target)
60
+ ["cd ~/rpmbuild/SPECS/",
61
+ "rpmbuild -bb #{target_spec_name(target)}",
62
+ ["find ~/rpmbuild/RPMS -name '*.rpm' ",
63
+ "-exec mv {} #{builder.docker.container_build_path} \\;"].join]
64
+ end
65
+
51
66
  def native_build_instructions(builder, target)
52
- version, release = target.package_version.split('-')
53
- source_name = "#{target.package_name}-#{version}"
54
- source_archive_path = Pathname.new('/package.tar.gz')
55
- target_spec_name = "#{target.package_name}.spec"
56
-
57
- ["cp -r #{builder.docker.container_package_path} /tmp/#{source_name}",
58
- "cd /tmp",
59
- "tar -zcvf #{source_archive_path} #{source_name}",
60
- "ln -fs #{builder.docker.container_build_path} ~/rpmbuild",
61
- "rpmdev-setuptree",
62
- "cp #{source_archive_path} ~/rpmbuild/SOURCES",
63
- "cp #{builder.docker.container_package_path.join(target_spec_name)} ~/rpmbuild/SPECS",
64
- "cd ~/rpmbuild/SPECS",
65
- "rpmbuild -ba #{target_spec_name}",
66
- "cp $(find #{builder.docker.container_build_path.join('RPMS')} -name '*.rpm') #{builder.docker.container_build_path}",
67
- ]
67
+ [*Array(rpmdev_setuptree_instructions(builder, target)),
68
+ "cp #{builder.docker.container_package_archive_path} ~/rpmbuild/SOURCES/",
69
+ "cp #{builder.docker.container_package_path.join(target_spec_name(target))} ~/rpmbuild/SPECS/",
70
+ *Array(build_rpm_instructions(builder, target))]
71
+ end
72
+
73
+ def patch_build_instructions(builder, target)
74
+ rpmchange_cmd = "rpmchange %{cmd} --specfile ~/rpmbuild/SPECS/#{target_spec_name(target)} %{args}"
75
+ get_release_cmd = rpmchange_cmd % {cmd: :tag, args: "--name release"}
76
+ set_release_cmd = rpmchange_cmd % {cmd: :tag, args: "--name release --value %{value}"}
77
+ changelog_cmd = rpmchange_cmd % {
78
+ cmd: :changelog,
79
+ args: "--append --name \"%{name}\" --email \"%{email}\" --message \"%{message}\""
80
+ }
81
+
82
+ [*Array(rpmdev_setuptree_instructions(builder, target)),
83
+ "yumdownloader --source #{target_package_spec(target)}",
84
+ "rpm -i *.rpm",
85
+ "gem install rpmchange",
86
+ set_release_cmd % {value: "$(#{get_release_cmd})buildizer#{target.package_version}"},
87
+ *target.patch.map {|patch| "cp #{patch} ~/rpmbuild/SOURCES/"},
88
+ *target.patch.map {|patch|
89
+ rpmchange_cmd % {cmd: :append,
90
+ args: "--section prep --value \"patch -p1 < %{_sourcedir}/#{patch}\""}
91
+ },
92
+ changelog_cmd % {name: target.maintainer,
93
+ email: target.maintainer_email,
94
+ message: 'Patch by buildizer'},
95
+ *Array(build_rpm_instructions(builder, target))]
96
+ end
97
+
98
+ def target_package_spec(target)
99
+ [target.package_name, target.package_version].compact.join('-')
68
100
  end
69
101
  end # Centos
70
102
  end # Image
@@ -34,6 +34,37 @@ module Buildizer
34
34
  res << '--deb-no-default-config-files'
35
35
  end
36
36
  end
37
+
38
+ def build_deb_instructions(builder, target)
39
+ ["DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage -b -us -uc -j#{builder.build_jobs}",
40
+ "cp ../*.deb #{builder.docker.container_build_path}"]
41
+ end
42
+
43
+ def native_build_instructions(builder, target)
44
+ source_archive_name = "#{target.package_name}_#{target.package_upstream_version}.orig.tar.gz"
45
+
46
+ [["ln -fs #{target.container_package_archive_path} ",
47
+ "#{target.container_package_path.dirname.join(source_archive_name)}"].join,
48
+ "cd #{target.container_package_path}",
49
+ *Array(build_deb_instructions(builder, target))]
50
+ end
51
+
52
+ def patch_build_instructions(builder, target)
53
+ ["apt-get source #{target_package_spec(target)}",
54
+ 'cd $(ls *.orig.tar* | ruby -ne "puts \$_.split(\\".orig.tar\\").first.gsub(\\"_\\", \\"-\\")")',
55
+ ["DEBFULLNAME=\"#{target.maintainer}\" DEBEMAIL=\"#{target.maintainer_email}\" ",
56
+ "debchange --newversion ",
57
+ "$(dpkg-parsechangelog | grep \"Version:\" | cut -d\" \" -f2-)buildizer#{target.patch_version} ",
58
+ "--distribution #{os_codename} \"Patch by buildizer\""].join,
59
+ *target.patch.map {|patch| "cp ../#{patch} debian/patches/"},
60
+ *target.patch.map {|patch| "sed -i \"/#{Regexp.escape(patch)}/d\" debian/patches/series"},
61
+ *target.patch.map {|patch| "echo #{patch} >> debian/patches/series"},
62
+ *Array(build_deb_instructions(builder, target))]
63
+ end
64
+
65
+ def target_package_spec(target)
66
+ [target.package_name, target.package_version].compact.join('=')
67
+ end
37
68
  end # Ubuntu
38
69
  end # Image
39
70
  end # Buildizer
@@ -0,0 +1,13 @@
1
+ module Buildizer
2
+ module Image
3
+ class Ubuntu1604 < Ubuntu
4
+ def initialize(docker, **kwargs)
5
+ super(docker, '16.04', **kwargs)
6
+ end
7
+
8
+ def os_codename
9
+ 'xenial'
10
+ end
11
+ end # Ubuntu1604
12
+ end # Image
13
+ end # Buildizer
@@ -1,9 +1,12 @@
1
1
  module Buildizer
2
2
  class Packager
3
+ using Refine
4
+
3
5
  attr_reader :package_path
4
6
  attr_reader :buildizer_conf_path
5
7
  attr_reader :options_path
6
8
  attr_reader :travis_path
9
+ attr_reader :work_path
7
10
  attr_reader :debug
8
11
 
9
12
  def initialize(options: {}, debug: false)
@@ -11,8 +14,9 @@ module Buildizer
11
14
  @buildizer_conf_path = package_path.join('Buildizer')
12
15
  @options_path = package_path.join('.buildizer.yml')
13
16
  @travis_path = package_path.join('.travis.yml')
17
+ @work_path = Pathname.new(ENV['BUILDIZER_WORK_PATH'] || '~/.buildizer').expand_path
14
18
  @_options = options
15
- @debug = debug
19
+ @debug = ENV['BUILDIZER_DEBUG'].nil? ? debug : ENV['BUILDIZER_DEBUG'].to_s.on?
16
20
  end
17
21
 
18
22
  def initialized?
@@ -53,6 +57,10 @@ module Buildizer
53
57
  builder.deploy
54
58
  end
55
59
 
60
+ def verify!
61
+ builder.verify
62
+ end
63
+
56
64
  def buildizer_conf
57
65
  @buildizer_conf ||= (YAML.load((buildizer_conf_path.read rescue "")) || {})
58
66
  end
@@ -155,12 +163,13 @@ git add -v .travis.yml
155
163
  buildizer_conf['package_version']
156
164
  end
157
165
 
158
- def package_version_tag
159
- ENV['TRAVIS_TAG'] || ENV['CI_BUILD_TAG']
166
+ def package_version_tag_required_for_deploy?
167
+ ENV['BUILDIZER_REQUIRE_TAG'].to_s.on?
160
168
  end
161
169
 
162
- def os_params(os)
163
- buildizer_conf['os'].to_h[os.to_s].to_h
170
+ def package_version_tag
171
+ res = ENV['TRAVIS_TAG'] || ENV['CI_BUILD_TAG']
172
+ if res.nil? then res elsif res.empty? then nil else res end
164
173
  end
165
174
 
166
175
  def before_prepare
@@ -199,40 +208,46 @@ git add -v .travis.yml
199
208
  buildizer_conf['image']
200
209
  end
201
210
 
202
- def package_cloud
203
- buildizer_conf['package_cloud']
211
+ def package_cloud_repo
212
+ ENV['PACKAGECLOUD'].to_s.split(',')
204
213
  end
205
214
 
206
- def package_cloud_token
207
- ENV['PACKAGECLOUD_TOKEN'] || begin
208
- raise Error, error: :input_error, message: "PACKAGECLOUD_TOKEN env variable required"
209
- end
215
+ def package_cloud_org
216
+ default_token = ENV['PACKAGECLOUD_TOKEN']
217
+ package_cloud_repo.map {|repo| repo.split('/').first}.uniq.map do |org|
218
+ [org, ENV["PACKAGECLOUD_TOKEN_#{org.upcase}"] || default_token]
219
+ end.to_h
210
220
  end
211
221
 
212
- def docker_username
213
- ENV['BUILDIZER_DOCKER_USERNAME'] || begin
214
- raise Error, error: :input_error, message: "BUILDIZER_DOCKER_USERNAME env variable required"
222
+ def package_cloud
223
+ tokens = package_cloud_org
224
+ package_cloud_repo.map do |repo|
225
+ org = repo.split('/').first
226
+ token = tokens[org]
227
+ {org: org, repo: repo, token: token}
215
228
  end
216
229
  end
217
230
 
218
- def docker_password
219
- ENV['BUILDIZER_DOCKER_PASSWORD'] || begin
220
- raise Error, error: :input_error, message: "BUILDIZER_DOCKER_PASSWORD env variable required"
221
- end
231
+ def docker_cache
232
+ return unless org = ENV['BUILDIZER_DOCKER_CACHE']
233
+ {username: ENV['BUILDIZER_DOCKER_CACHE_USERNAME'],
234
+ password: ENV['BUILDIZER_DOCKER_CACHE_PASSWORD'],
235
+ email: ENV['BUILDIZER_DOCKER_CACHE_EMAIL'],
236
+ server: ENV['BUILDIZER_DOCKER_CACHE_SERVER'],
237
+ org: org}
222
238
  end
223
239
 
224
- def docker_email
225
- ENV['BUILDIZER_DOCKER_EMAIL'] || begin
226
- raise Error, error: :input_error, message: "BUILDIZER_DOCKER_EMAIL env variable required"
227
- end
240
+ def maintainer
241
+ buildizer_conf['maintainer']
228
242
  end
229
243
 
230
244
  def builder
231
245
  @builder ||= begin
232
246
  build_type = buildizer_conf['build_type']
233
- raise Error, error: :input_error, message: "no build_type given" unless build_type
247
+ raise Error, error: :input_error, message: "build_type is not defined" unless build_type
234
248
  klass = {fpm: Builder::Fpm,
235
- native: Builder::Native}[build_type.to_s.to_sym]
249
+ native: Builder::Native,
250
+ patch: Builder::Patch}[build_type.to_s.to_sym]
236
251
  raise Error, error: :input_error, message: "unknown build_type '#{build_type}'" unless klass
237
252
  klass.new(self)
238
253
  end