releasy 0.2.0rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/.gitignore +17 -0
  2. data/.yardopts +4 -0
  3. data/CHANGELOG.md +24 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.md +165 -0
  7. data/Rakefile +31 -0
  8. data/bin/7z.sfx +0 -0
  9. data/bin/releasy +7 -0
  10. data/lib/releasy.rb +18 -0
  11. data/lib/releasy/archivers.rb +12 -0
  12. data/lib/releasy/archivers/archiver.rb +74 -0
  13. data/lib/releasy/archivers/dmg.rb +18 -0
  14. data/lib/releasy/archivers/exe.rb +23 -0
  15. data/lib/releasy/archivers/seven_zip.rb +12 -0
  16. data/lib/releasy/archivers/tar_archiver.rb +14 -0
  17. data/lib/releasy/archivers/tar_bzip2.rb +13 -0
  18. data/lib/releasy/archivers/tar_gzip.rb +13 -0
  19. data/lib/releasy/archivers/zip.rb +12 -0
  20. data/lib/releasy/builders.rb +12 -0
  21. data/lib/releasy/builders/builder.rb +52 -0
  22. data/lib/releasy/builders/ocra_builder.rb +43 -0
  23. data/lib/releasy/builders/osx_app.rb +158 -0
  24. data/lib/releasy/builders/source.rb +27 -0
  25. data/lib/releasy/builders/windows_builder.rb +65 -0
  26. data/lib/releasy/builders/windows_folder.rb +47 -0
  27. data/lib/releasy/builders/windows_folder_from_ruby_dist.rb +150 -0
  28. data/lib/releasy/builders/windows_installer.rb +117 -0
  29. data/lib/releasy/builders/windows_standalone.rb +37 -0
  30. data/lib/releasy/cli.rb +12 -0
  31. data/lib/releasy/cli/install_sfx.rb +66 -0
  32. data/lib/releasy/dsl_wrapper.rb +71 -0
  33. data/lib/releasy/mixins/exec.rb +14 -0
  34. data/lib/releasy/mixins/has_archivers.rb +37 -0
  35. data/lib/releasy/mixins/has_gemspecs.rb +42 -0
  36. data/lib/releasy/mixins/register.rb +47 -0
  37. data/lib/releasy/project.rb +300 -0
  38. data/lib/releasy/version.rb +3 -0
  39. data/lib/releasy/windows_wrapper_maker.rb +90 -0
  40. data/lib/releasy/windows_wrapper_maker/Gemfile +8 -0
  41. data/media/releasy.png +0 -0
  42. data/releasy.gemspec +37 -0
  43. data/test/releasy/archivers_test.rb +65 -0
  44. data/test/releasy/builders/data/Info.plist +51 -0
  45. data/test/releasy/builders/data/Main.rb +12 -0
  46. data/test/releasy/builders/data/relapse_runner.rb +1 -0
  47. data/test/releasy/builders/data/set_app_executable.sh +3 -0
  48. data/test/releasy/builders/helpers/helper.rb +47 -0
  49. data/test/releasy/builders/ocra_builder_test.rb +37 -0
  50. data/test/releasy/builders/osx_app_test.rb +107 -0
  51. data/test/releasy/builders/source_test.rb +43 -0
  52. data/test/releasy/builders/windows_builder_test.rb +26 -0
  53. data/test/releasy/builders/windows_folder_from_ruby_dist_test.rb +105 -0
  54. data/test/releasy/builders/windows_folder_test.rb +56 -0
  55. data/test/releasy/builders/windows_installer_test.rb +62 -0
  56. data/test/releasy/builders/windows_standalone_test.rb +58 -0
  57. data/test/releasy/cli/install_sfx_test.rb +90 -0
  58. data/test/releasy/dsl_wrapper_test.rb +79 -0
  59. data/test/releasy/integration/source_test.rb +122 -0
  60. data/test/releasy/mixins/register_test.rb +52 -0
  61. data/test/releasy/project_test.rb +198 -0
  62. data/test/releasy/windows_wrapper_maker_test.rb +61 -0
  63. data/test/teststrap.rb +52 -0
  64. data/test/yard_test.rb +61 -0
  65. data/test_project/Gemfile +9 -0
  66. data/test_project/LICENSE.txt +1 -0
  67. data/test_project/README.txt +2 -0
  68. data/test_project/bin/test_app +3 -0
  69. data/test_project/lib/test_app.rb +6 -0
  70. data/test_project/lib/test_app/stuff.rb +1 -0
  71. data/test_project/test_app.icns +0 -0
  72. data/test_project/test_app.ico +0 -0
  73. data/wrappers/put_wrappers_here_for_testing.txt +17 -0
  74. metadata +236 -0
@@ -0,0 +1,14 @@
1
+ module Releasy
2
+ module Mixins
3
+ # Adds an {#exec} method that prints out if the project is verbose.
4
+ module Exec
5
+ protected
6
+ def exec(command)
7
+ puts command if project.verbose?
8
+ result = %x[#{command}]
9
+ puts result if project.verbose?
10
+ result
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,37 @@
1
+ module Releasy
2
+ module Mixins
3
+ # An object that owns one or more instances of {Archivers::Archiver}
4
+ module HasArchivers
5
+ # Add an archive type to be generated for each of your outputs.
6
+ # @see Project#initialize
7
+ # @param type [:exe, :"7z", :tar_bz2, :tar_gz, :zip]
8
+ # @return [Project] self
9
+ def add_archive(type, &block)
10
+ raise ArgumentError, "Unsupported archive format #{type.inspect}" unless Archivers.has_type? type
11
+ raise ConfigError, "Already have archive format #{type.inspect}" if archivers.any? {|a| a.type == type }
12
+
13
+ archiver = Archivers[type].new(respond_to?(:project) ? project : self)
14
+ archivers << archiver
15
+
16
+ if block_given?
17
+ if block.arity == 0
18
+ DSLWrapper.new(archiver, &block)
19
+ else
20
+ yield archiver
21
+ end
22
+ end
23
+
24
+ archiver
25
+ end
26
+
27
+ protected
28
+ def archivers; @archivers ||= []; end
29
+
30
+ protected
31
+ # @return [Array<Archiver>]
32
+ def active_archivers
33
+ archivers
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,42 @@
1
+ module Releasy
2
+ module Mixins
3
+ # An object that manages a list of {#gemspecs}
4
+ module HasGemspecs
5
+ # @return [Array<Gem>] List of gemspecs used by the application. Will default to the gems in the `default` Bundler group or, if Bundler isn't used, all gems loaded by rubygems.
6
+ attr_accessor :gemspecs
7
+
8
+ protected
9
+ def setup
10
+ @gemspecs = if defined? Bundler
11
+ Bundler.definition.specs_for([:default]).to_a
12
+ else
13
+ Gem.loaded_specs.values
14
+ end
15
+ super
16
+ end
17
+
18
+ protected
19
+ # Don't include binary gems already in the .app or bundler, since it will get confused.
20
+ def vendored_gem_names(ignored_gems); (gemspecs.map(&:name) - ignored_gems).sort; end
21
+
22
+ protected
23
+ def copy_gems(gems, destination)
24
+ puts "Copying source gems from system" if project.verbose?
25
+
26
+ gems_dir = "#{destination}/gems"
27
+ specs_dir = "#{destination}/specifications"
28
+ mkdir_p gems_dir
29
+ mkdir_p specs_dir
30
+
31
+ gems.each do |gem|
32
+ spec = gemspecs.find {|g| g.name == gem }
33
+ gem_dir = spec.full_gem_path
34
+ puts "Copying gem: #{spec.name} #{spec.version}" if project.verbose?
35
+ cp_r gem_dir, gems_dir
36
+ spec_file = File.expand_path("../../specifications/#{File.basename gem_dir}.gemspec", gem_dir)
37
+ cp_r spec_file, specs_dir
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,47 @@
1
+ require 'forwardable'
2
+
3
+ module Releasy
4
+ module Mixins
5
+ # Maintains a registry of classes within a module.
6
+ #
7
+ # @example
8
+ # module Frogs
9
+ # extend Releasy::Mixins::Register
10
+ # end
11
+ #
12
+ # class Frogs::BlueFrog
13
+ # TYPE = :blue # Type must be defined before registering.
14
+ # Frogs.register self
15
+ # end
16
+ #
17
+ # class Frogs::RedFrog
18
+ # TYPE = :red # Type must be defined before registering.
19
+ # Frogs.register self
20
+ # end
21
+ #
22
+ # Frogs[:blue] #=> Frogs::BlueFrog
23
+ # Frogs.has_type? :blue #=> true
24
+ # Frogs.types #=> [:blue, :red]
25
+ # Frogs.values #=> [Frogs::BlueFrog, Frogs::RedFrog]
26
+ module Register
27
+ include Enumerable
28
+ extend Forwardable
29
+
30
+ def_delegators :registered, :[], :each, :values
31
+ def_delegator :registered, :has_key?, :has_type?
32
+ def_delegator :registered, :keys, :types
33
+
34
+ # Register a class with this register of classes of that type.
35
+ # @param klass [Object] Object, which is defined within the namespace being registered with.
36
+ def register(klass)
37
+ raise TypeError, "Can only register classes" unless klass.is_a? Class
38
+ raise ArgumentError, "Can't register a class not within this module" unless klass.name.split('::')[0...-1].join('::') == name
39
+ raise ArgumentError, "To register, a class must have TYPE defined" unless klass.const_defined? :TYPE
40
+ registered[klass::TYPE] = klass
41
+ end
42
+
43
+ protected
44
+ def registered; @registered ||= {}; end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,300 @@
1
+ require 'releasy/dsl_wrapper'
2
+ require 'releasy/builders'
3
+ require 'releasy/archivers'
4
+ require "releasy/mixins/has_archivers"
5
+
6
+ module Releasy
7
+ # A description of the Ruby application that is being build for release and what packages to make from it.
8
+ #
9
+ # @attr underscored_name [String] Project name underscored (as used in file names), which will be derived from {#name}, but can be manually set.
10
+ # @attr underscored_version [String] Version number, underscored so it can be used in file names, which will be derived from {#version}, but can be manually set.
11
+ # @attr executable [String] Name of executable to run (defaults to 'bin/<underscored_name>')
12
+ # @attr_reader folder_base [String] The path to the folders to create. All variations of output will be based on extending this path.
13
+ # @attr files [Rake::FileList] List of files to include in package.
14
+ # @attr exposed_files [Rake::FileList] Files which should always be copied into the archive folder root, so they are always visible to the user. e.g readme, change-log and/or license files.
15
+ class Project
16
+ include Rake::DSL
17
+ include Mixins::HasArchivers
18
+
19
+ DEFAULT_PACKAGE_FOLDER = "pkg"
20
+
21
+ attr_writer :underscored_name, :underscored_version, :executable
22
+
23
+ # @return [String] Name of the application, such as "My Application".
24
+ attr_accessor :name
25
+ # @return [String] Version number as a string (for example, "1.2.0").
26
+ attr_accessor :version
27
+ # @return [String] Folder to output to (defaults to 'pkg/')
28
+ attr_accessor :output_path
29
+
30
+ # Make the tasks give more detailed output.
31
+ # @return [nil]
32
+ def verbose; @verbose = true; nil; end
33
+ # Make the tasks give less detailed output.
34
+ # @return [nil]
35
+ def quiet; @verbose = false; nil; end
36
+ def verbose?; @verbose; end
37
+
38
+ # Create MD5 hashes for created archives.
39
+ # @return [nil]
40
+ def create_md5s; @create_md5s = true; nil; end
41
+ def create_md5s?; @create_md5s; end
42
+ protected :create_md5s?
43
+
44
+ # Verbosity of the console output.
45
+ # @return [Boolean] True to make the tasks output more information.
46
+ def verbose?; @verbose; end
47
+
48
+ def exposed_files; @exposed_files; end
49
+ def exposed_files=(files); @exposed_files = Rake::FileList.new files; end
50
+
51
+ def files; @files; end
52
+ def files=(files); @files = Rake::FileList.new files; end
53
+
54
+ # @return [String]
55
+ def to_s; "<#{self.class}#{name ? " #{name}" : ""}#{version ? " #{version}" : ""}>"; end
56
+
57
+ def underscored_name
58
+ if @underscored_name or @name.nil?
59
+ @underscored_name
60
+ else
61
+ @name.strip.downcase.gsub(/[^a-z0-9_\- ]/i, '').split(/[\-_ ]+/).join("_")
62
+ end
63
+ end
64
+
65
+ def underscored_version
66
+ if @underscored_version or @version.nil?
67
+ @underscored_version
68
+ else
69
+ @version.gsub(".", "_")
70
+ end
71
+ end
72
+
73
+ def executable
74
+ if @executable or underscored_name.nil?
75
+ @executable
76
+ else
77
+ "bin/#{underscored_name}"
78
+ end
79
+ end
80
+
81
+ # Can be used with or without a block to generate building and packaging tasks.
82
+ #
83
+ # @overload initialize(&block)
84
+ # Using a block, the API is more terse and the tasks are automatically generated
85
+ # when the block is closed (Uses a {DSLWrapper}). This is the preferred syntax!
86
+ #
87
+ # @example
88
+ # Releasy::Project.new do
89
+ # name "My Application"
90
+ # version "1.2.4"
91
+ # add_build :source do
92
+ # add_archive :tar_gz do
93
+ # extension ".tgz"
94
+ # end
95
+ # end
96
+ # end
97
+ #
98
+ # @yield [] Block is evaluated in context of a {DSLWrapper} wrapping self.
99
+ #
100
+ # @overload initialize(&block)
101
+ # Using a block that takes a parameter, self is passed, and so the API is similar to a Gem::Specification.
102
+ # The tasks are automatically generated when the block is closed
103
+ #
104
+ # @example
105
+ # Releasy::Project.new do |p|
106
+ # p.name = "My Application"
107
+ # p.version = "1.2.4"
108
+ # p.add_build :source do |b|
109
+ # b.add_archive :tar_gz do |a|
110
+ # a.extension = ".tgz"
111
+ # end
112
+ # end
113
+ # end
114
+ #
115
+ # @yieldparam project [Project] new project
116
+ #
117
+ # @overload initialize
118
+ # Without using blocks, the {Project} can be accessed directly. It is recommended that a block is used.
119
+ #
120
+ # @example
121
+ # project = Releasy::Project.new
122
+ # project.name = "My Application"
123
+ # project.version = "1.2.4"
124
+ # builder = project.add_build :source
125
+ # archiver = builder.add_archive :zip
126
+ # archiver.extension = ".tgz"
127
+ # project.generate_tasks # This has to be done manually.
128
+ #
129
+ def initialize(&block)
130
+ super()
131
+
132
+ @builders = []
133
+ @links = {}
134
+ @files = Rake::FileList.new
135
+ @exposed_files = Rake::FileList.new
136
+ @output_path = DEFAULT_PACKAGE_FOLDER
137
+ @verbose = true
138
+ @create_md5s = false
139
+ @name = @underscored_name = @underscored_version = nil
140
+ @version = @executable = nil
141
+
142
+ setup
143
+
144
+ if block_given?
145
+ if block.arity == 0
146
+ DSLWrapper.new(self, &block)
147
+ else
148
+ yield self
149
+ end
150
+
151
+ generate_tasks
152
+ end
153
+ end
154
+
155
+ # Add a type of output to produce. Must define at least one of these.
156
+ # @see #initialize
157
+ # @param type [:osx_app, :source, :windows_folder, :windows_folder_from_ruby_dist, :windows_installer, :windows_standalone]
158
+ # @return [Project] self
159
+ def add_build(type, &block)
160
+ raise ArgumentError, "Unsupported output type #{type}" unless Builders.has_type? type
161
+ raise ConfigError, "Already have output #{type.inspect}" if @builders.any? {|b| b.type == type }
162
+
163
+ builder = Builders[type].new(self)
164
+ @builders << builder
165
+
166
+ if block_given?
167
+ if block.arity == 0
168
+ DSLWrapper.new(builder, &block)
169
+ else
170
+ yield builder
171
+ end
172
+ end
173
+
174
+ builder
175
+ end
176
+
177
+ # Add a link file to be included in the win32 releases. Will create the file _title.url_ for you.
178
+ #
179
+ # @param url [String] Url to link to.
180
+ # @param title [String] Name of file to create.
181
+ # @return [Project] self
182
+ def add_link(url, title)
183
+ @links[url] = title
184
+
185
+ self
186
+ end
187
+
188
+ # Generates all tasks required by the user. Automatically called at the end of the block, if {#initialize} is given a block.
189
+ # @return [Project] self
190
+ def generate_tasks
191
+ raise ConfigError, "Must specify at least one valid output for this OS with #add_build before tasks can be generated" if @builders.empty?
192
+
193
+ # Even if there are builders specified, none may work on this platform.
194
+ return if active_builders.empty?
195
+
196
+ build_outputs = []
197
+ build_groups = Hash.new {|h, k| h[k] = [] }
198
+
199
+ active_builders.each do |builder|
200
+ builder.send :generate_tasks
201
+ task_name = "build:#{builder.type.to_s.tr("_", ":")}"
202
+
203
+ if builder.type.to_s =~ /_/
204
+ task_group = builder.send :task_group
205
+ build_groups[task_group] << task_name
206
+ build_outputs << "build:#{task_group}"
207
+ else
208
+ build_outputs << task_name
209
+ end
210
+ end
211
+
212
+ build_groups.each_pair do |group, tasks|
213
+ desc "Build all #{group} outputs"
214
+ task "build:#{group}" => tasks
215
+ end
216
+
217
+ desc "Build all outputs"
218
+ task "build" => build_outputs
219
+
220
+ generate_archive_tasks
221
+
222
+ self
223
+ end
224
+
225
+
226
+ def folder_base
227
+ File.join(output_path, "#{underscored_name}#{version ? "_#{underscored_version}" : ""}")
228
+ end
229
+
230
+ protected
231
+ # Only allow access to this from Builder
232
+ # @return [Hash]
233
+ def links; @links; end
234
+
235
+ protected
236
+ def setup; end
237
+
238
+ protected
239
+ # @return [Array<Builder>]
240
+ def active_builders
241
+ @builders.find_all(&:valid_for_platform?)
242
+ end
243
+
244
+ protected
245
+ # Generates the general tasks for compressing folders.
246
+ def generate_archive_tasks
247
+ return if active_builders.empty?
248
+
249
+ windows_tasks = []
250
+ osx_tasks = []
251
+ top_level_tasks = []
252
+ active_builders.each do |builder|
253
+ output_task = builder.type.to_s.sub '_', ':'
254
+
255
+ archivers = active_archivers(builder)
256
+ archivers.each do |archiver|
257
+ archiver.send :generate_tasks, output_task, builder.send(:folder)
258
+ end
259
+
260
+ desc "Package all #{builder.type}"
261
+ task "package:#{output_task}" => archivers.map {|c| "package:#{output_task}:#{c.type}" }
262
+
263
+ case output_task
264
+ when /^windows:/
265
+ windows_tasks << "package:#{output_task}"
266
+ top_level_tasks << "package:windows" unless top_level_tasks.include? "package:windows"
267
+ when /^osx:/
268
+ osx_tasks << "package:#{output_task}"
269
+ top_level_tasks << "package:osx" unless top_level_tasks.include? "package:osx"
270
+ else
271
+ top_level_tasks << "package:#{output_task}"
272
+ end
273
+ end
274
+
275
+ unless windows_tasks.empty?
276
+ desc "Package all Windows"
277
+ task "package:windows" => windows_tasks
278
+ end
279
+
280
+ unless osx_tasks.empty?
281
+ desc "Package all OS X"
282
+ task "package:osx" => osx_tasks
283
+ end
284
+
285
+ desc "Package all"
286
+ task "package" => top_level_tasks
287
+
288
+ self
289
+ end
290
+
291
+ protected
292
+ def active_archivers(builder)
293
+ # Use archivers specifically set on the builder and those set globally that aren't on the builder.
294
+ archivers = builder.send(:active_archivers)
295
+ archiver_types = archivers.map(&:type)
296
+
297
+ archivers + super().reject {|a| archiver_types.include? a.type }
298
+ end
299
+ end
300
+ end
@@ -0,0 +1,3 @@
1
+ module Releasy
2
+ VERSION = "0.2.0rc1"
3
+ end