omnibus 2.0.2 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +2 -0
  3. data/.travis.yml +0 -2
  4. data/CHANGELOG.md +26 -6
  5. data/Gemfile +7 -0
  6. data/Guardfile +10 -0
  7. data/README.md +103 -160
  8. data/Rakefile +6 -1
  9. data/docs/Building on OSX.md +66 -0
  10. data/docs/omnibus-build-cache.md +75 -0
  11. data/lib/omnibus.rb +9 -13
  12. data/lib/omnibus/artifact.rb +1 -13
  13. data/lib/omnibus/assets/README-logo.png +0 -0
  14. data/lib/omnibus/assets/logo.psd +0 -0
  15. data/lib/omnibus/builder.rb +1 -0
  16. data/lib/omnibus/cli/application.rb +17 -4
  17. data/lib/omnibus/cli/build.rb +6 -4
  18. data/lib/omnibus/config.rb +33 -0
  19. data/lib/omnibus/exceptions.rb +20 -14
  20. data/lib/omnibus/health_check.rb +2 -0
  21. data/lib/omnibus/install_path_cache.rb +106 -0
  22. data/lib/omnibus/library.rb +18 -1
  23. data/lib/omnibus/packagers/base.rb +228 -0
  24. data/lib/omnibus/packagers/mac_dmg.rb +215 -0
  25. data/lib/omnibus/packagers/mac_pkg.rb +129 -253
  26. data/lib/omnibus/project.rb +89 -95
  27. data/lib/omnibus/s3_cacher.rb +4 -7
  28. data/lib/omnibus/software.rb +47 -83
  29. data/lib/omnibus/sugar.rb +49 -0
  30. data/lib/omnibus/templates/.kitchen.yml.erb +3 -0
  31. data/lib/omnibus/templates/Berksfile.erb +4 -0
  32. data/lib/omnibus/templates/Gemfile.erb +1 -1
  33. data/lib/omnibus/templates/mac_dmg/background.png +0 -0
  34. data/lib/omnibus/templates/mac_dmg/icon.png +0 -0
  35. data/lib/omnibus/templates/mac_pkg/background.png +0 -0
  36. data/lib/omnibus/templates/mac_pkg/license.html.erb +1 -0
  37. data/lib/omnibus/templates/mac_pkg/welcome.html.erb +9 -0
  38. data/lib/omnibus/templates/omnibus.rb.example.erb +31 -4
  39. data/lib/omnibus/version.rb +1 -1
  40. data/omnibus.gemspec +5 -4
  41. data/spec/fixtures/sample/files/mac_dmg/Resources/background.png +0 -0
  42. data/spec/fixtures/sample/files/mac_dmg/Resources/icon.png +0 -0
  43. data/spec/fixtures/sample/files/mac_pkg/Resources/background.png +0 -0
  44. data/spec/fixtures/sample/files/mac_pkg/Resources/license.html +1 -0
  45. data/spec/fixtures/sample/files/mac_pkg/Resources/welcome.html +9 -0
  46. data/{functional/fixtures/mac_pkg/package-scripts/functional-test-project → spec/fixtures/sample/package-scripts/sample}/postinstall +0 -0
  47. data/spec/functional/packagers/mac_spec.rb +74 -0
  48. data/spec/spec_helper.rb +14 -3
  49. data/spec/sugar_spec.rb +20 -0
  50. data/spec/{artifact_spec.rb → unit/artifact_spec.rb} +2 -3
  51. data/spec/{build_version_spec.rb → unit/build_version_spec.rb} +0 -0
  52. data/spec/{config_spec.rb → unit/config_spec.rb} +4 -0
  53. data/spec/{fetchers → unit/fetchers}/git_fetcher_spec.rb +0 -0
  54. data/spec/{fetchers → unit/fetchers}/net_fetcher_spec.rb +0 -0
  55. data/spec/unit/install_path_cache_spec.rb +175 -0
  56. data/spec/unit/library_spec.rb +67 -0
  57. data/spec/{omnibus_spec.rb → unit/omnibus_spec.rb} +0 -0
  58. data/spec/{overrides_spec.rb → unit/overrides_spec.rb} +0 -0
  59. data/spec/{package_release_spec.rb → unit/package_release_spec.rb} +0 -0
  60. data/spec/unit/packagers/base_spec.rb +221 -0
  61. data/spec/unit/packagers/mac_pkg_spec.rb +163 -0
  62. data/spec/{project_spec.rb → unit/project_spec.rb} +0 -14
  63. data/spec/{s3_cacher_spec.rb → unit/s3_cacher_spec.rb} +0 -0
  64. data/spec/{software_spec.rb → unit/software_spec.rb} +0 -1
  65. metadata +122 -103
  66. data/functional/fixtures/mac_pkg/files/mac_pkg/Resources/background.png +0 -0
  67. data/functional/fixtures/mac_pkg/files/mac_pkg/Resources/license.html +0 -1
  68. data/functional/fixtures/mac_pkg/files/mac_pkg/Resources/welcome.html +0 -1
  69. data/functional/packagers/mac_pkg_spec.rb +0 -72
  70. data/lib/omnibus/clean_tasks.rb +0 -28
  71. data/spec/packagers/mac_pkg_spec.rb +0 -262
@@ -27,7 +27,24 @@ module Omnibus
27
27
  end
28
28
 
29
29
  def component_added(component)
30
- @components << component unless @components.include?(component)
30
+ unless @components.find { |c| c.name == component.name }
31
+ @components << component
32
+ end
33
+ end
34
+
35
+ def build_order
36
+ head = []
37
+ tail = []
38
+ @components.each do |component|
39
+ if head.length == 0
40
+ head << component
41
+ elsif @project.dependencies.include?(component.name)
42
+ tail << component
43
+ else
44
+ head << component
45
+ end
46
+ end
47
+ [head, tail].flatten
31
48
  end
32
49
 
33
50
  def version_map
@@ -0,0 +1,228 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2014 Chef Software, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require 'fileutils'
19
+ require 'forwardable'
20
+ require 'omnibus/util'
21
+
22
+ module Omnibus
23
+ class Packager::Base
24
+ include Util
25
+
26
+ extend Forwardable
27
+
28
+ # The Omnibus::Project instance that we're packaging.
29
+ attr_reader :project
30
+
31
+ # !@method name
32
+ # @return (see Project#name)
33
+ def_delegator :@project, :name
34
+
35
+ # !@method version
36
+ # @return (see Project#build_version)
37
+ def_delegator :@project, :build_version, :version
38
+
39
+ # !@method iteration
40
+ # @return (see Project#iteration)
41
+ def_delegator :@project, :iteration, :iteration
42
+
43
+ # !@method identifier
44
+ # @return (see Project#mac_pkg_identifier)
45
+ def_delegator :@project, :mac_pkg_identifier, :identifier
46
+
47
+ # !@method install_path
48
+ # @return (see Project#install_path)
49
+ def_delegator :@project, :install_path, :install_path
50
+
51
+ # !@method scripts
52
+ # @return (see Project#package_scripts_path)
53
+ def_delegator :@project, :package_scripts_path, :scripts
54
+
55
+ # !@method files_path
56
+ # @return (see Project#files_path)
57
+ def_delegator :@project, :files_path
58
+
59
+ # !@method package_dir
60
+ # @return (see Project#package_dir)
61
+ def_delegator :@project, :package_dir
62
+
63
+ # The commands/steps to setup the file system.
64
+ def self.setup(&block)
65
+ if block_given?
66
+ @setup = block
67
+ else
68
+ @setup
69
+ end
70
+ end
71
+
72
+ # The commands/steps to validate any arguments.
73
+ def self.validate(&block)
74
+ if block_given?
75
+ @validate = block
76
+ else
77
+ @validate
78
+ end
79
+ end
80
+
81
+ # The commands/steps to build the package.
82
+ def self.build(&block)
83
+ if block_given?
84
+ @build = block
85
+ else
86
+ @build || fail(AbstractMethod.new("#{self.class.name}.build"))
87
+ end
88
+ end
89
+
90
+ # The commands/steps to cleanup any temporary files/directories.
91
+ def self.clean(&block)
92
+ if block_given?
93
+ @clean = block
94
+ else
95
+ @clean
96
+ end
97
+ end
98
+
99
+ # Create a new packager object.
100
+ #
101
+ # @param [Project] project
102
+ def initialize(project)
103
+ @project = project
104
+ end
105
+
106
+ #
107
+ # Generation methods
108
+ # ------------------------------
109
+
110
+ # Create a directory at the given +path+.
111
+ #
112
+ # @param [String] path
113
+ def create_directory(path)
114
+ FileUtils.mkdir_p(path)
115
+ path
116
+ end
117
+
118
+ # Remove the directory at the given +path+.
119
+ #
120
+ # @param [String] path
121
+ def remove_directory(path)
122
+ FileUtils.rm_rf(path)
123
+ end
124
+
125
+ # Purge the directory of all contents.
126
+ #
127
+ # @param [String] path
128
+ def purge_directory(path)
129
+ remove_directory(path)
130
+ create_directory(path)
131
+ end
132
+
133
+ # Copy the +source+ file to the +destination+.
134
+ #
135
+ # @param [String] source
136
+ # @param [String] destination
137
+ def copy_file(source, destination)
138
+ FileUtils.cp(source, destination)
139
+ destination
140
+ end
141
+
142
+ # Remove the file at the given path.
143
+ #
144
+ # @param [String] pah
145
+ def remove_file(path)
146
+ FileUtils.rm_f(path)
147
+ end
148
+
149
+ # Execute the command using shellout!
150
+ #
151
+ # @param [String] command
152
+ def execute(command)
153
+ shellout!(command, timeout: 3600, cwd: staging_dir)
154
+ end
155
+
156
+ #
157
+ # Validations
158
+ # ------------------------------
159
+
160
+ # Validate the presence of a file.
161
+ #
162
+ # @param [String] path
163
+ def assert_presence!(path)
164
+ fail MissingAsset.new(path) unless File.exist?(path)
165
+ end
166
+
167
+ # Execute this packager by running the following phases in order:
168
+ #
169
+ # - setup
170
+ # - validate
171
+ # - build
172
+ # - clean
173
+ #
174
+ def run!
175
+ instance_eval(&self.class.validate) if self.class.validate
176
+ instance_eval(&self.class.setup) if self.class.setup
177
+ instance_eval(&self.class.build) if self.class.build
178
+ instance_eval(&self.class.clean) if self.class.clean
179
+ end
180
+
181
+ # The ending name of this package on disk. +Omnibus::Project+ uses this to
182
+ # generate metadata about the package after it is built.
183
+ #
184
+ # @return [String]
185
+ def package_name
186
+ fail AbstractMethod.new("#{self.class.name}#package_name")
187
+ end
188
+
189
+ private
190
+
191
+ # The path to the directory where we can throw staged files.
192
+ #
193
+ # @return [String]
194
+ def staging_dir
195
+ File.expand_path("#{project.package_tmp}/#{underscore_name}")
196
+ end
197
+
198
+ # The path to a local resource on disk.
199
+ #
200
+ # @param [String]
201
+ # the name or path of the resource
202
+ # @return [String]
203
+ def resource(path)
204
+ File.expand_path("#{resources_path}/#{path}")
205
+ end
206
+
207
+ # The path to all the resources on the local file system.
208
+ #
209
+ # @return [String]
210
+ def resources_path
211
+ File.expand_path("#{project.files_path}/#{underscore_name}/Resources")
212
+ end
213
+
214
+ # The underscored equivalent of this class. This is mostly used by file
215
+ # paths.
216
+ #
217
+ # @return [String]
218
+ def underscore_name
219
+ @underscore_name ||= self.class.name
220
+ .split('::')
221
+ .last
222
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
223
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
224
+ .tr('-', '_')
225
+ .downcase
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,215 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2014 Chef Software, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ module Omnibus
19
+ class Packager::MacDmg < Packager::Base
20
+ attr_reader :packager
21
+
22
+ validate do
23
+ assert_presence!(resource('background.png'))
24
+ assert_presence!(resource('icon.png'))
25
+ end
26
+
27
+ setup do
28
+ create_directory(dmg_stage)
29
+ copy_assets_to_dmg
30
+ end
31
+
32
+ build do
33
+ copy_assets_to_dmg
34
+ create_writable_dmg
35
+ attach_dmg
36
+ set_volume_icon
37
+ prettify_dmg
38
+ compress_dmg
39
+ set_dmg_icon
40
+ end
41
+
42
+ clean do
43
+ remove_file("#{staging_dir}/tmp.icns")
44
+ remove_file("#{staging_dir}/tmp.rsrc")
45
+ end
46
+
47
+ #
48
+ # Create a new DMG packager.
49
+ #
50
+ # @param [Packager::MacPkg] mac_packager
51
+ #
52
+ def initialize(mac_packager)
53
+ @packager = mac_packager
54
+ super(mac_packager.project)
55
+ end
56
+
57
+ #
58
+ # Copy the assets in the local omnibus project into a staging folder that
59
+ # will soon become our writable dmg.
60
+ #
61
+ def copy_assets_to_dmg
62
+ # Copy the compiled pkg into the dmg
63
+ copy_file(packager.final_pkg, "#{dmg_stage}/#{project.name}.pkg")
64
+
65
+ # Copy support files
66
+ support = create_directory("#{dmg_stage}/.support")
67
+ copy_file(resource('background.png'), "#{support}/background.png")
68
+ end
69
+
70
+ #
71
+ # Create a writable dmg we can put assets on.
72
+ #
73
+ def create_writable_dmg
74
+ execute <<-EOH.gsub(/^ {8}/, '')
75
+ hdiutil create \\
76
+ -srcfolder "#{dmg_stage}" \\
77
+ -volname "#{project.name}" \\
78
+ -fs HFS+ \\
79
+ -fsargs "-c c=64,a=16,e=16" \\
80
+ -format UDRW \\
81
+ -size 512000k \\
82
+ "#{writable_dmg}"
83
+ EOH
84
+ end
85
+
86
+ #
87
+ # Attach the dmg, storing a reference to the device for later use.
88
+ #
89
+ def attach_dmg
90
+ @device = execute(<<-EOH.gsub(/^ {8}/, '')).stdout.strip
91
+ hdiutil attach \\
92
+ -readwrite \\
93
+ -noverify \\
94
+ -noautoopen \\
95
+ "#{writable_dmg}" | egrep '^/dev/' | sed 1q | awk '{print $1}'
96
+ EOH
97
+ end
98
+
99
+ #
100
+ # Create the icon for the volume using sips.
101
+ #
102
+ def set_volume_icon
103
+ execute <<-EOH.gsub(/^ {8}/, '')
104
+ # Generate the icns
105
+ mkdir tmp.iconset
106
+ sips -z 16 16 #{resource('icon.png')} --out tmp.iconset/icon_16x16.png
107
+ sips -z 32 32 #{resource('icon.png')} --out tmp.iconset/icon_16x16@2x.png
108
+ sips -z 32 32 #{resource('icon.png')} --out tmp.iconset/icon_32x32.png
109
+ sips -z 64 64 #{resource('icon.png')} --out tmp.iconset/icon_32x32@2x.png
110
+ sips -z 128 128 #{resource('icon.png')} --out tmp.iconset/icon_128x128.png
111
+ sips -z 256 256 #{resource('icon.png')} --out tmp.iconset/icon_128x128@2x.png
112
+ sips -z 256 256 #{resource('icon.png')} --out tmp.iconset/icon_256x256.png
113
+ sips -z 512 512 #{resource('icon.png')} --out tmp.iconset/icon_256x256@2x.png
114
+ sips -z 512 512 #{resource('icon.png')} --out tmp.iconset/icon_512x512.png
115
+ sips -z 1024 1024 #{resource('icon.png')} --out tmp.iconset/icon_512x512@2x.png
116
+ iconutil -c icns tmp.iconset
117
+
118
+ # Copy it over
119
+ cp tmp.icns "/Volumes/#{project.name}/.VolumeIcon.icns"
120
+
121
+ # Source the icon
122
+ SetFile -a C "/Volumes/#{project.name}"
123
+ EOH
124
+ end
125
+
126
+ #
127
+ # Use Applescript to setup the DMG with pretty logos and colors.
128
+ #
129
+ def prettify_dmg
130
+ execute <<-EOH.gsub(/ ^{8}/, '')
131
+ echo '
132
+ tell application "Finder"
133
+ tell disk "'#{project.name}'"
134
+ open
135
+ set current view of container window to icon view
136
+ set toolbar visible of container window to false
137
+ set statusbar visible of container window to false
138
+ set the bounds of container window to {#{project.config[:dmg_window_bounds]}}
139
+ set theViewOptions to the icon view options of container window
140
+ set arrangement of theViewOptions to not arranged
141
+ set icon size of theViewOptions to 72
142
+ set background picture of theViewOptions to file ".support:'background.png'"
143
+ delay 5
144
+ set position of item "'#{project.name}.pkg'" of container window to {#{project.config[:dmg_pkg_position]}}
145
+ update without registering applications
146
+ delay 5
147
+ end tell
148
+ end tell
149
+ ' | osascript
150
+ EOH
151
+ end
152
+
153
+ #
154
+ # Compress the dmg using hdiutil and zlib.
155
+ #
156
+ def compress_dmg
157
+ execute <<-EOH.gsub(/ ^{8}/, '')
158
+ chmod -Rf go-w /Volumes/#{project.name}
159
+ sync
160
+ hdiutil detach "#{@device}"
161
+ hdiutil convert \\
162
+ "#{writable_dmg}" \\
163
+ -format UDZO \\
164
+ -imagekey zlib-level=9 \\
165
+ -o "#{final_dmg}"
166
+ rm -rf "#{writable_dmg}"
167
+ EOH
168
+ end
169
+
170
+ #
171
+ # Set the dmg icon to our custom icon.
172
+ #
173
+ def set_dmg_icon
174
+ execute <<-EOH.gsub(/^ {8}/, '')
175
+ # Convert the png to an icon
176
+ sips -i "#{resource('icon.png')}"
177
+
178
+ # Extract the icon into its own resource
179
+ DeRez -only icns "#{resource('icon.png')}" > tmp.rsrc
180
+
181
+ # Append the icon reosurce to the DMG
182
+ Rez -append tmp.rsrc -o "#{final_dmg}"
183
+
184
+ # Source the icon
185
+ SetFile -a C "#{final_dmg}"
186
+ EOH
187
+ end
188
+
189
+ # @see Base#package_name
190
+ def package_name
191
+ "#{name}-#{version}-#{iteration}.dmg"
192
+ end
193
+
194
+ # The path to the folder that we should stage.
195
+ #
196
+ # @return [String]
197
+ def dmg_stage
198
+ File.expand_path("#{staging_dir}/dmg")
199
+ end
200
+
201
+ # The path to the writable dmg on disk.
202
+ #
203
+ # @return [String]
204
+ def writable_dmg
205
+ File.expand_path("#{staging_dir}/#{name}-writable.dmg")
206
+ end
207
+
208
+ # The path where the final dmg will be produced.
209
+ #
210
+ # @return [String]
211
+ def final_dmg
212
+ File.expand_path("#{project.package_dir}/#{name}-#{version}-#{iteration}.dmg")
213
+ end
214
+ end
215
+ end