releasy 0.2.0rc1

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.
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