rails_packager 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +34 -0
  4. data/exe/rails_package +25 -0
  5. data/lib/rails_packager.rb +8 -0
  6. data/lib/rails_packager/command.rb +84 -0
  7. data/lib/rails_packager/command_parser.rb +66 -0
  8. data/lib/rails_packager/engine.rb +4 -0
  9. data/lib/rails_packager/runner.rb +138 -0
  10. data/lib/rails_packager/util.rb +30 -0
  11. data/lib/rails_packager/version.rb +3 -0
  12. data/lib/tasks/rails_packager_tasks.rake +8 -0
  13. data/test/dummy/Gemfile +2 -0
  14. data/test/dummy/README.rdoc +28 -0
  15. data/test/dummy/Rakefile +6 -0
  16. data/test/dummy/app/assets/javascripts/application.js +13 -0
  17. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  18. data/test/dummy/app/controllers/application_controller.rb +5 -0
  19. data/test/dummy/app/helpers/application_helper.rb +2 -0
  20. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  21. data/test/dummy/bin/bundle +3 -0
  22. data/test/dummy/bin/rails +4 -0
  23. data/test/dummy/bin/rake +4 -0
  24. data/test/dummy/bin/setup +29 -0
  25. data/test/dummy/config.ru +4 -0
  26. data/test/dummy/config/application.rb +22 -0
  27. data/test/dummy/config/boot.rb +5 -0
  28. data/test/dummy/config/database.yml +25 -0
  29. data/test/dummy/config/environment.rb +5 -0
  30. data/test/dummy/config/environments/development.rb +41 -0
  31. data/test/dummy/config/environments/production.rb +79 -0
  32. data/test/dummy/config/environments/test.rb +42 -0
  33. data/test/dummy/config/initializers/assets.rb +11 -0
  34. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  35. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  36. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  37. data/test/dummy/config/initializers/inflections.rb +16 -0
  38. data/test/dummy/config/initializers/mime_types.rb +4 -0
  39. data/test/dummy/config/initializers/session_store.rb +3 -0
  40. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  41. data/test/dummy/config/locales/en.yml +23 -0
  42. data/test/dummy/config/routes.rb +56 -0
  43. data/test/dummy/config/secrets.yml +22 -0
  44. data/test/dummy/db/development.sqlite3 +0 -0
  45. data/test/dummy/db/schema.rb +15 -0
  46. data/test/dummy/db/test.sqlite3 +0 -0
  47. data/test/dummy/log/development.log +7 -0
  48. data/test/dummy/log/test.log +21747 -0
  49. data/test/dummy/public/404.html +67 -0
  50. data/test/dummy/public/422.html +67 -0
  51. data/test/dummy/public/500.html +66 -0
  52. data/test/dummy/public/favicon.ico +0 -0
  53. data/test/files/environment-variable-in-one-command-merges-with-env.yml +9 -0
  54. data/test/files/environment-variable-in-one-command.yml +5 -0
  55. data/test/files/environment-variable-replacement.yml +7 -0
  56. data/test/files/execution-path.yml +3 -0
  57. data/test/files/failure-integration.yml +6 -0
  58. data/test/files/files-is-within-quotes.yml +2 -0
  59. data/test/files/files-not-its-own-argument.yml +2 -0
  60. data/test/files/fully-customized.yml +13 -0
  61. data/test/files/missing-command-name.yml +5 -0
  62. data/test/files/quotes-in-command.yml +2 -0
  63. data/test/files/simple-integration.yml +7 -0
  64. data/test/integration_test.rb +224 -0
  65. data/test/jruby_dummy/Jarfile +1 -0
  66. data/test/jruby_dummy/README.rdoc +28 -0
  67. data/test/jruby_dummy/Rakefile +6 -0
  68. data/test/jruby_dummy/app/assets/javascripts/application.js +13 -0
  69. data/test/jruby_dummy/app/assets/stylesheets/application.css +15 -0
  70. data/test/jruby_dummy/app/controllers/application_controller.rb +5 -0
  71. data/test/jruby_dummy/app/helpers/application_helper.rb +2 -0
  72. data/test/jruby_dummy/app/views/layouts/application.html.erb +14 -0
  73. data/test/jruby_dummy/bin/bundle +3 -0
  74. data/test/jruby_dummy/bin/rails +4 -0
  75. data/test/jruby_dummy/bin/rake +4 -0
  76. data/test/jruby_dummy/bin/setup +29 -0
  77. data/test/jruby_dummy/config.ru +4 -0
  78. data/test/jruby_dummy/config/application.rb +22 -0
  79. data/test/jruby_dummy/config/boot.rb +5 -0
  80. data/test/jruby_dummy/config/database.yml +23 -0
  81. data/test/jruby_dummy/config/environment.rb +5 -0
  82. data/test/jruby_dummy/config/environments/development.rb +41 -0
  83. data/test/jruby_dummy/config/environments/production.rb +79 -0
  84. data/test/jruby_dummy/config/environments/test.rb +42 -0
  85. data/test/jruby_dummy/config/initializers/assets.rb +11 -0
  86. data/test/jruby_dummy/config/initializers/backtrace_silencers.rb +7 -0
  87. data/test/jruby_dummy/config/initializers/cookies_serializer.rb +3 -0
  88. data/test/jruby_dummy/config/initializers/filter_parameter_logging.rb +4 -0
  89. data/test/jruby_dummy/config/initializers/inflections.rb +16 -0
  90. data/test/jruby_dummy/config/initializers/mime_types.rb +4 -0
  91. data/test/jruby_dummy/config/initializers/session_store.rb +3 -0
  92. data/test/jruby_dummy/config/initializers/wrap_parameters.rb +14 -0
  93. data/test/jruby_dummy/config/locales/en.yml +23 -0
  94. data/test/jruby_dummy/config/routes.rb +56 -0
  95. data/test/jruby_dummy/config/secrets.yml +22 -0
  96. data/test/jruby_dummy/db/schema.rb +15 -0
  97. data/test/jruby_dummy/public/404.html +67 -0
  98. data/test/jruby_dummy/public/422.html +67 -0
  99. data/test/jruby_dummy/public/500.html +66 -0
  100. data/test/jruby_dummy/public/favicon.ico +0 -0
  101. data/test/rails_packager/command_parser_test.rb +74 -0
  102. data/test/rails_packager/runner_test.rb +225 -0
  103. data/test/rails_packager/util_test.rb +84 -0
  104. data/test/rails_packager_test.rb +7 -0
  105. data/test/support/util_helper.rb +20 -0
  106. data/test/test_helper.rb +22 -0
  107. metadata +279 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 496700d2c25783048dddb01349963eb88ac182ae
4
+ data.tar.gz: 6d14489090ca004941521543918efa1d859980ae
5
+ SHA512:
6
+ metadata.gz: 7f3ee49843a6fc52874788e9e88c16d3374e0d4051693cc8423c1393125d8cc8a28ae7fe12f47940ec1fce9717731afc191c11f27a08ad9a1fbcd1d7aaadd73d
7
+ data.tar.gz: 5dcc909d6791bd0f3ddcc650f413fd72e702a185b3729cf816e6a7de9590d6a1c86fca4ab5dec5e43c1d45149f593dad633beeb2907243cadfeb8a7c73c9f33c
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Mike Virata-Stone
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'RailsPackager'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
data/exe/rails_package ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ require "pathname"
3
+ require "optparse"
4
+ require "rails_packager"
5
+
6
+ options = {}
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: rails_package [options]"
9
+
10
+ opts.on("-v", "--verbose", "Include verbose output") do |v|
11
+ options[:verbose] = v
12
+ end
13
+
14
+ opts.on("-n", "--name NAME", "Change the NAME used for packaging the app") do |name|
15
+ options[:name] = name
16
+ end
17
+ end.parse!
18
+
19
+ abort "No Gemfile detected, are you in the root of a Rails project?" unless File.exist?("Gemfile")
20
+ abort "The 'rails' gem is missing from the Gemfile, are you in the root of a Rails project?" unless File.read("Gemfile") =~ /\brails\b/
21
+ dir = Pathname.new(".").realpath
22
+ config_path = dir.join(".rails-package")
23
+ config_file = config_path if config_path.exist?
24
+ runner = RailsPackager::Runner.new(config_file: config_file, dir: dir, name: options[:name])
25
+ runner.execute(verbose: options[:verbose])
@@ -0,0 +1,8 @@
1
+ require "rails_packager/engine" if defined?(Rails)
2
+
3
+ module RailsPackager
4
+ autoload :Command, "rails_packager/command"
5
+ autoload :CommandParser, "rails_packager/command_parser"
6
+ autoload :Runner, "rails_packager/runner"
7
+ autoload :Util, "rails_packager/util"
8
+ end
@@ -0,0 +1,84 @@
1
+ require "active_support/core_ext/object/blank"
2
+
3
+ module RailsPackager
4
+ class Command
5
+ def self.replace_variables(runner, value, allow_files: false)
6
+ return nil if value.nil?
7
+ result = value.dup
8
+ result.gsub!(/\$\{(\w+)\}/) { |m| ENV.fetch($1, "") }
9
+ result["@{name}"] = runner.name if result["@{name}"]
10
+
11
+ if allow_files
12
+ raise ArgumentError, "@{files} must be a singular argument" if result["@{files}"] && result != "@{files}"
13
+ raise ArgumentError, "@{files_file} must be a singular argument" if result["@{files_file}"] && result != "@{files_file}"
14
+
15
+ if result == "@{files}"
16
+ result = runner.files
17
+ elsif result == "@{files_file}"
18
+ result = runner.files_file
19
+ end
20
+ end
21
+
22
+ result
23
+ end
24
+
25
+ def self.parse(runner, value)
26
+ parsed = CommandParser.parse(value)
27
+ new(parsed.name, *parsed.args, runner: runner, env: parsed.env)
28
+ end
29
+
30
+ def initialize(name, *args, runner:, env: {})
31
+ @runner = runner
32
+ @name = replace_variables(name)
33
+ @args = args
34
+ @env = env
35
+ end
36
+
37
+ def name
38
+ raise ArgumentError, "Invalid command: empty command name is not valid" if @name.blank?
39
+ @name
40
+ end
41
+
42
+ def dir
43
+ @runner.dir
44
+ end
45
+
46
+ def env
47
+ @runner.env.merge(@env).inject({}) do |result, (key, value)|
48
+ result[key] = replace_variables(value)
49
+ result
50
+ end
51
+ end
52
+
53
+ def args
54
+ @args.map do |arg|
55
+ replace_variables(arg, allow_files: true)
56
+ end.flatten
57
+ end
58
+
59
+ def execute(verbose: false)
60
+ command_name = name
61
+ command_args = args
62
+ command_line = ([command_name] + command_args).join(" ")
63
+ puts "$ #{command_line}" if verbose
64
+ system(env, command_name, *command_args, chdir: dir)
65
+ @status = $?
66
+ STDERR.puts "ERROR: '#{command_line}' returned error code: #{exit_code}" unless successful?
67
+ successful?
68
+ end
69
+
70
+ def exit_code
71
+ @status.exitstatus
72
+ end
73
+
74
+ def successful?
75
+ @status.success?
76
+ end
77
+
78
+ private
79
+
80
+ def replace_variables(value, allow_files: false)
81
+ RailsPackager::Command.replace_variables(@runner, value, allow_files: allow_files)
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,66 @@
1
+ module RailsPackager
2
+ class CommandParser
3
+ attr_reader :env, :name, :args
4
+
5
+ def self.parse(command)
6
+ new(command).tap(&:parse)
7
+ end
8
+
9
+ def initialize(command)
10
+ @parsed = false
11
+ @unparsed = command
12
+ end
13
+
14
+ def parse
15
+ return if @parsed
16
+ @parsed = true
17
+
18
+ result =
19
+ if @unparsed.is_a?(Array)
20
+ @env = parse_env(@unparsed.last)
21
+ parse_command(@unparsed.first)
22
+ else
23
+ @env = {}
24
+ parse_command(@unparsed)
25
+ end
26
+
27
+ raise ArgumentError, "Empty command is not allowed" if result.empty?
28
+ @name = result.shift
29
+ @args = result
30
+ end
31
+
32
+ private
33
+
34
+ def parse_env(options)
35
+ {}.tap do |result|
36
+ if options.include?("unsetenv")
37
+ options["unsetenv"].each do |var|
38
+ result[var] = nil
39
+ end
40
+ end
41
+
42
+ if options.include?("env")
43
+ result.merge!(options["env"])
44
+ end
45
+ end
46
+ end
47
+
48
+ def parse_command(command, result = [])
49
+ command = command.strip
50
+ return result if command.empty?
51
+
52
+ if command[0] == "'"
53
+ value, remaining = command[1..-1].split("'", 2)
54
+ raise ArgumentError, "Mismatched single quote" unless remaining
55
+ elsif command[0] == '"'
56
+ value, remaining = command[1..-1].split('"', 2)
57
+ raise ArgumentError, "Mismatched single quote" unless remaining
58
+ else
59
+ value, remaining = command.split(" ", 2)
60
+ end
61
+
62
+ result << value
63
+ parse_command(remaining || "", result)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,4 @@
1
+ module RailsPackager
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,138 @@
1
+ require "active_support/core_ext/hash/keys"
2
+ require "active_support/core_ext/object/blank"
3
+ require "tempfile"
4
+ require "yaml"
5
+
6
+ module RailsPackager
7
+ class Runner
8
+ attr_reader :includes, :excludes, :env, :dir, :name, :before, :package, :after
9
+
10
+ # This command is inserted after bundle install when jbundler usage is detected
11
+ JBUNDLE_COMMAND = "jbundle install --vendor".freeze
12
+
13
+ DEFAULT_CONFIG = RailsPackager::Util.deep_freeze(
14
+ env: {},
15
+ exclude: ["**/.git", "tmp"],
16
+ before: [
17
+ ["bundle install --deployment --without development test", {
18
+ "unsetenv" => ["RUBYOPT", "RUBYLIB", "BUNDLER_ORIG_GEM_PATH", "BUNDLER_ORIG_PATH"],
19
+ "env" => { "GEM_PATH" => "${BUNDLER_ORIG_GEM_PATH}" }
20
+ }],
21
+ "gem install bundler --install-dir vendor/bundle",
22
+ ["bundle exec rake assets:precompile", {
23
+ "unsetenv" => ["RUBYOPT", "RUBYLIB", "BUNDLER_ORIG_GEM_PATH", "BUNDLER_ORIG_PATH"],
24
+ "env" => {
25
+ "RAILS_ENV" => "production",
26
+ "GEM_PATH" => "${BUNDLER_ORIG_GEM_PATH}"
27
+ }
28
+ }]
29
+ ],
30
+ package: "tar --no-recursion --files-from @{files_file} -zcvf @{name}.tar.gz"
31
+ )
32
+
33
+ def initialize(dir:, name: nil, config_file: nil)
34
+ @files_file_tempfile = nil
35
+ @dir = dir
36
+ @name = name
37
+
38
+ config =
39
+ if config_file
40
+ YAML.load_file(config_file)
41
+ else
42
+ {}
43
+ end
44
+
45
+ load_config(config)
46
+ end
47
+
48
+ def files
49
+ result = Dir.glob(File.join(dir, "**/*"), File::FNM_DOTMATCH).map do |file|
50
+ file.sub(File.join(dir, "/"), "")
51
+ end
52
+
53
+ # Drop special directory files
54
+ result.reject! { |f| f =~ %r{/\.\.?\z} || f =~ /\A\.\.?\z/ }
55
+
56
+ result.select! { |f| includes.any? { |i| RailsPackager::Util.glob_match?(i, f) } } if includes
57
+ result.reject! { |f| excludes.any? { |e| RailsPackager::Util.glob_match?(e, f) } } if excludes
58
+ result
59
+ end
60
+
61
+ def files_file
62
+ @files_file_tempfile ||=
63
+ begin
64
+ Tempfile.new("rails_packager_file_names").tap do |tempfile|
65
+ files.each { |f| tempfile.puts f }
66
+ tempfile.close
67
+ end
68
+ end
69
+
70
+ @files_file_tempfile.path
71
+ end
72
+
73
+ def commands
74
+ before + [package] + after
75
+ end
76
+
77
+ def execute(verbose: false)
78
+ commands.each { |cmd| break unless cmd.execute(verbose: verbose) }
79
+ ensure
80
+ close
81
+ end
82
+
83
+ def close
84
+ if @files_file_tempfile
85
+ @files_file_tempfile.unlink
86
+ @files_file_tempfile = nil
87
+ end
88
+ end
89
+
90
+ def successful?
91
+ commands.all?(&:successful?)
92
+ end
93
+
94
+ def exit_code
95
+ errored = commands.find { |cmd| !cmd.successful? }
96
+
97
+ if errored
98
+ errored.exit_code
99
+ else
100
+ 0
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def replace_variables(value)
107
+ RailsPackager::Command.replace_variables(self, value)
108
+ end
109
+
110
+ def load_config(config)
111
+ config = config.symbolize_keys
112
+ customized_before = config.include?(:before)
113
+ config = DEFAULT_CONFIG.merge(config)
114
+
115
+ if !customized_before && jbundler_in_use?
116
+ config[:before] = config[:before].dup
117
+ config[:before].insert(2, JBUNDLE_COMMAND)
118
+ end
119
+
120
+ @includes = config[:include]
121
+ @excludes = config[:exclude]
122
+
123
+ @env = config[:env].inject({}) do |result, (key, value)|
124
+ result[key] = replace_variables(value)
125
+ result
126
+ end
127
+
128
+ @name = name.presence || replace_variables(config.fetch(:name) { File.basename(File.realpath(dir)) })
129
+ @before = config.fetch(:before, []).map { |x| RailsPackager::Command.parse(self, x) }
130
+ @after = config.fetch(:after, []).map { |x| RailsPackager::Command.parse(self, x) }
131
+ @package = RailsPackager::Command.parse(self, config[:package])
132
+ end
133
+
134
+ def jbundler_in_use?
135
+ File.exist? File.join(dir, "Jarfile")
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,30 @@
1
+ module RailsPackager
2
+ module Util
3
+ module_function
4
+
5
+ def deep_freeze(object)
6
+ case object
7
+ when Hash
8
+ object.freeze
9
+ object.each { |k, v| deep_freeze(k); deep_freeze(v) }
10
+ when Array
11
+ object.freeze
12
+ object.each { |y| deep_freeze(y) }
13
+ when Symbol
14
+ # Unneeded
15
+ else
16
+ object.freeze
17
+ end
18
+ end
19
+
20
+ TERMINAL_FILES = deep_freeze([".", "/", ""])
21
+
22
+ def glob_match?(glob, file)
23
+ if File.fnmatch(glob, file, File::FNM_PATHNAME | File::FNM_DOTMATCH)
24
+ true
25
+ elsif !TERMINAL_FILES.include?(file)
26
+ glob_match?(glob, File.dirname(file))
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module RailsPackager
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,8 @@
1
+ desc "Package this Rails project"
2
+ task :package, [:name] => :environment do |t, args|
3
+ require "rails_packager"
4
+ config_path = Rails.root.join(".rails-package")
5
+ config_file = config_path if config_path.exist?
6
+ runner = RailsPackager::Runner.new(config_file: config_file, dir: Rails.root, name: args[:name].presence)
7
+ runner.execute(verbose: ENV["verbose"] == "true")
8
+ end