suspenders 20230113.0 → 20240516.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (171) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +83 -176
  3. data/Rakefile +13 -0
  4. data/lib/generators/suspenders/accessibility_generator.rb +24 -0
  5. data/lib/generators/suspenders/advisories_generator.rb +32 -0
  6. data/lib/generators/suspenders/ci_generator.rb +57 -0
  7. data/lib/generators/suspenders/email_generator.rb +56 -0
  8. data/lib/generators/suspenders/environments/development_generator.rb +50 -0
  9. data/lib/generators/suspenders/environments/production_generator.rb +27 -0
  10. data/lib/generators/suspenders/environments/test_generator.rb +39 -0
  11. data/lib/generators/suspenders/factories_generator.rb +64 -0
  12. data/lib/generators/suspenders/inline_svg_generator.rb +24 -0
  13. data/lib/generators/suspenders/install/web_generator.rb +71 -0
  14. data/lib/generators/suspenders/jobs_generator.rb +34 -0
  15. data/lib/generators/suspenders/lint_generator.rb +94 -0
  16. data/lib/generators/suspenders/prerequisites_generator.rb +19 -0
  17. data/lib/generators/suspenders/rake_generator.rb +25 -0
  18. data/lib/generators/suspenders/setup_generator.rb +14 -0
  19. data/lib/generators/suspenders/styles_generator.rb +84 -0
  20. data/lib/generators/suspenders/tasks_generator.rb +20 -0
  21. data/lib/generators/suspenders/testing_generator.rb +113 -0
  22. data/lib/generators/suspenders/views_generator.rb +38 -0
  23. data/lib/generators/templates/ci/ci.yml.tt +148 -0
  24. data/lib/generators/templates/ci/dependabot.yml +7 -0
  25. data/lib/generators/templates/email/email_interceptor.rb +11 -0
  26. data/lib/generators/templates/factories/factories_spec.rb +7 -0
  27. data/lib/generators/templates/factories/factories_test.rb +9 -0
  28. data/lib/generators/templates/install/web/CONTRIBUTING.md +94 -0
  29. data/lib/generators/templates/lint/config_better_html.yml +2 -0
  30. data/lib/generators/templates/lint/config_initializers_better_html.rb +9 -0
  31. data/lib/generators/templates/lint/erb-lint.yml +63 -0
  32. data/lib/generators/templates/lint/erblint.rake +47 -0
  33. data/lib/generators/templates/lint/eslintrc.json +7 -0
  34. data/lib/generators/templates/lint/package.json +4 -0
  35. data/lib/generators/templates/lint/prettierignore +1 -0
  36. data/lib/generators/templates/lint/prettierrc +11 -0
  37. data/lib/generators/templates/lint/rubocop.yml.tt +7 -0
  38. data/lib/generators/templates/prerequisites/node-version.tt +1 -0
  39. data/lib/generators/templates/setup/bin_setup.rb +39 -0
  40. data/lib/generators/templates/styles/postcss.config.js +11 -0
  41. data/lib/generators/templates/testing/driver.rb +5 -0
  42. data/lib/generators/templates/views/flashes.html.erb +7 -0
  43. data/lib/install/web.rb +54 -0
  44. data/lib/suspenders/cleanup/generate_readme.rb +165 -0
  45. data/lib/suspenders/cleanup/organize_gemfile.rb +134 -0
  46. data/lib/suspenders/engine.rb +5 -0
  47. data/lib/suspenders/generators.rb +126 -0
  48. data/lib/suspenders/railtie.rb +4 -0
  49. data/lib/suspenders/version.rb +4 -6
  50. data/lib/suspenders.rb +9 -33
  51. data/lib/tasks/suspenders.rake +37 -0
  52. metadata +82 -173
  53. data/.ruby-version +0 -1
  54. data/CONTRIBUTING.md +0 -59
  55. data/GOALS.md +0 -65
  56. data/LICENSE +0 -21
  57. data/NEWS.md +0 -738
  58. data/RELEASING.md +0 -18
  59. data/bin/suspenders +0 -50
  60. data/docs/heroku_deploy.md +0 -19
  61. data/docs/rails_7.md +0 -5
  62. data/lib/suspenders/actions/strip_comments_action.rb +0 -254
  63. data/lib/suspenders/actions.rb +0 -106
  64. data/lib/suspenders/adapters/heroku.rb +0 -136
  65. data/lib/suspenders/app_builder.rb +0 -267
  66. data/lib/suspenders/exit_on_failure.rb +0 -19
  67. data/lib/suspenders/generators/accessibility_generator.rb +0 -12
  68. data/lib/suspenders/generators/advisories_generator.rb +0 -15
  69. data/lib/suspenders/generators/analytics_generator.rb +0 -24
  70. data/lib/suspenders/generators/app_generator.rb +0 -215
  71. data/lib/suspenders/generators/base.rb +0 -60
  72. data/lib/suspenders/generators/ci_generator.rb +0 -32
  73. data/lib/suspenders/generators/db_optimizations_generator.rb +0 -18
  74. data/lib/suspenders/generators/factories_generator.rb +0 -22
  75. data/lib/suspenders/generators/forms_generator.rb +0 -18
  76. data/lib/suspenders/generators/inline_svg_generator.rb +0 -14
  77. data/lib/suspenders/generators/jobs_generator.rb +0 -37
  78. data/lib/suspenders/generators/js_driver_generator.rb +0 -18
  79. data/lib/suspenders/generators/json_generator.rb +0 -14
  80. data/lib/suspenders/generators/lint_generator.rb +0 -14
  81. data/lib/suspenders/generators/production/compression_generator.rb +0 -14
  82. data/lib/suspenders/generators/production/deployment_generator.rb +0 -16
  83. data/lib/suspenders/generators/production/email_generator.rb +0 -34
  84. data/lib/suspenders/generators/production/force_tls_generator.rb +0 -11
  85. data/lib/suspenders/generators/production/manifest_generator.rb +0 -25
  86. data/lib/suspenders/generators/production/single_redirect.rb +0 -15
  87. data/lib/suspenders/generators/production/timeout_generator.rb +0 -22
  88. data/lib/suspenders/generators/profiler_generator.rb +0 -35
  89. data/lib/suspenders/generators/runner_generator.rb +0 -48
  90. data/lib/suspenders/generators/staging/pull_requests_generator.rb +0 -25
  91. data/lib/suspenders/generators/static_generator.rb +0 -14
  92. data/lib/suspenders/generators/stylelint_generator.rb +0 -71
  93. data/lib/suspenders/generators/stylesheet_base_generator.rb +0 -15
  94. data/lib/suspenders/generators/testing_generator.rb +0 -43
  95. data/lib/suspenders/generators/views_generator.rb +0 -25
  96. data/templates/Gemfile.erb +0 -49
  97. data/templates/Procfile +0 -2
  98. data/templates/README.md.erb +0 -28
  99. data/templates/_analytics.html.erb +0 -8
  100. data/templates/_css_overrides.html.erb +0 -7
  101. data/templates/_flashes.html.erb +0 -7
  102. data/templates/active_job.rb +0 -14
  103. data/templates/application.postcss.css +0 -1
  104. data/templates/bin_auto_migrate +0 -5
  105. data/templates/bin_deploy +0 -10
  106. data/templates/bin_setup +0 -28
  107. data/templates/bin_setup_review_app.erb +0 -21
  108. data/templates/bin_yarn +0 -18
  109. data/templates/bundler_audit.rake +0 -4
  110. data/templates/capybara_silence_puma.rb +0 -1
  111. data/templates/chromedriver.rb +0 -27
  112. data/templates/circle.yml.erb +0 -6
  113. data/templates/config_locales_en.yml.erb +0 -19
  114. data/templates/descriptions/advisories.md +0 -5
  115. data/templates/descriptions/analytics.md +0 -4
  116. data/templates/descriptions/ci.md +0 -4
  117. data/templates/descriptions/compression.md +0 -4
  118. data/templates/descriptions/db_optimizations.md +0 -2
  119. data/templates/descriptions/deployment.md +0 -5
  120. data/templates/descriptions/email.md +0 -9
  121. data/templates/descriptions/factories.md +0 -12
  122. data/templates/descriptions/force_tls.md +0 -1
  123. data/templates/descriptions/forms.md +0 -1
  124. data/templates/descriptions/inline_svg.md +0 -2
  125. data/templates/descriptions/jobs.md +0 -3
  126. data/templates/descriptions/js_driver.md +0 -4
  127. data/templates/descriptions/json.md +0 -1
  128. data/templates/descriptions/lint.md +0 -3
  129. data/templates/descriptions/manifest.md +0 -2
  130. data/templates/descriptions/profiler.md +0 -7
  131. data/templates/descriptions/pull_requests.md +0 -4
  132. data/templates/descriptions/runner.md +0 -10
  133. data/templates/descriptions/single_redirect.md +0 -1
  134. data/templates/descriptions/static.md +0 -5
  135. data/templates/descriptions/stylelint.md +0 -3
  136. data/templates/descriptions/stylesheet_base.md +0 -1
  137. data/templates/descriptions/testing.md +0 -9
  138. data/templates/descriptions/timeout.md +0 -4
  139. data/templates/descriptions/views.md +0 -8
  140. data/templates/email.rb +0 -3
  141. data/templates/errors.rb +0 -35
  142. data/templates/flashes_helper.rb +0 -5
  143. data/templates/hound.yml +0 -15
  144. data/templates/json_encoding.rb +0 -1
  145. data/templates/oj.rb +0 -3
  146. data/templates/partials/ci_simplecov.rb +0 -14
  147. data/templates/partials/db_optimizations_configuration.rb +0 -7
  148. data/templates/partials/deployment_readme.md +0 -8
  149. data/templates/partials/email_smtp.rb +0 -2
  150. data/templates/partials/profiler_readme.md +0 -8
  151. data/templates/partials/pull_requests_config.rb +0 -5
  152. data/templates/partials/runner_readme.md +0 -31
  153. data/templates/partials/runner_setup.rb +0 -2
  154. data/templates/postcss.config.js +0 -8
  155. data/templates/postgresql_database.yml.erb +0 -20
  156. data/templates/rack_mini_profiler.rb +0 -7
  157. data/templates/rails_helper.rb +0 -25
  158. data/templates/sample_env +0 -12
  159. data/templates/secrets.yml +0 -8
  160. data/templates/smtp.rb +0 -9
  161. data/templates/spec_helper.rb +0 -25
  162. data/templates/suspenders_gitignore +0 -18
  163. data/templates/suspenders_layout.html.erb.erb +0 -23
  164. /data/{templates → lib/generators/templates/factories}/factories.rb +0 -0
  165. /data/{templates → lib/generators/templates/factories}/factory_bot_rspec.rb +0 -0
  166. /data/{templates → lib/generators/templates/inline_svg}/inline_svg.rb +0 -0
  167. /data/{templates → lib/generators/templates/lint}/stylelintrc.json +0 -0
  168. /data/{templates → lib/generators/templates/tasks}/dev.rake +0 -0
  169. /data/{templates → lib/generators/templates/testing}/action_mailer.rb +0 -0
  170. /data/{templates → lib/generators/templates/testing}/i18n.rb +0 -0
  171. /data/{templates/shoulda_matchers_config_rspec.rb → lib/generators/templates/testing/shoulda_matchers.rb} +0 -0
data/RELEASING.md DELETED
@@ -1,18 +0,0 @@
1
- # Releasing
2
-
3
- 1. Update `NEWS.md` to reflect the changes since last release.
4
- 2. Update `lib/suspenders/version.rb` file accordingly.
5
- 3. Commit changes. There shouldn't be code changes, and thus CI doesn't need to
6
- run; you can add `[ci skip]` to the commit message.
7
- 4. Tag the release: `git tag vVERSION -a -s`. The tag message should contain the
8
- appropriate `NEWS.md` subsection.
9
- 5. Push changes: `git push --tags`
10
- 6. Build and publish to rubygems:
11
- ```sh
12
- gem build suspenders.gemspec
13
- gem push suspenders-*.gem
14
- ```
15
- 7. Add a new GitHub release:
16
- https://github.com/thoughtbot/suspenders/releases/new?tag=vVERSION
17
- 8. Announce the new release, making sure to thank the contributors who helped
18
- shape this version!
data/bin/suspenders DELETED
@@ -1,50 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'pathname'
3
-
4
- source_path = (Pathname.new(__FILE__).dirname + '../lib').expand_path
5
- $LOAD_PATH << source_path
6
-
7
- activate_rails_version = ->(rails_version) do
8
- rails_bin_path = Gem.activate_bin_path("railties", "rails", rails_version)
9
- rails_path = File.expand_path("../..", rails_bin_path)
10
- $LOAD_PATH.unshift(rails_path)
11
- end
12
-
13
- if str = ARGV.first
14
- str = str.b[/\A_(.*)_\z/, 1]
15
-
16
- if str && Gem::Version.correct?(str)
17
- rails_version = str
18
- ARGV.shift
19
-
20
- begin
21
- activate_rails_version.call(rails_version)
22
- rescue Gem::GemNotFoundException
23
- abort "Suspenders error: Unable to find Rails version #{rails_version}"
24
- end
25
- else
26
- require "suspenders/version"
27
-
28
- spec = Gem::Specification.find_by_name("rails", Suspenders::RAILS_VERSION)
29
-
30
- activate_rails_version.call(spec.version.to_s)
31
- end
32
- end
33
-
34
- require "suspenders"
35
-
36
- if ARGV.empty?
37
- puts "Please provide a path for the new application"
38
- puts
39
- puts "See --help for more info"
40
- exit 0
41
- elsif ["-v", "--version"].include? ARGV[0]
42
- puts Suspenders::VERSION
43
- exit 0
44
- end
45
-
46
- templates_root = File.expand_path(File.join("..", "templates"), File.dirname(__FILE__))
47
- Suspenders::AppGenerator.source_root templates_root
48
- Suspenders::AppGenerator.source_paths << Rails::Generators::AppGenerator.source_root << templates_root
49
-
50
- Suspenders::AppGenerator.start
@@ -1,19 +0,0 @@
1
- # Deploying to Heroku
2
-
3
- ## Setup
4
-
5
- Deploying to Heroku requires two additional steps:
6
-
7
- 1. Set the following environment variables:
8
-
9
- - `ASSET_HOST`: `siteURL.herokuapp.com`
10
- - `APPLICATION_HOST`: `siteURL.herokuapp.com`
11
- - `SMTP_ADDRESS`: `smtp.example.com`
12
- - `SMTP_DOMAIN`: `example.com`
13
- - `SMTP_USERNAME`: `username`
14
- - `SMTP_PASSWORD`: `password`
15
- - `AUTO_MIGRATE_DB`: `true`
16
-
17
- ## Execution
18
-
19
- - Use the `./bin/deploy` convention for deploying to Heroku
data/docs/rails_7.md DELETED
@@ -1,5 +0,0 @@
1
- # Rails 7
2
-
3
- To create a new Rails 7 app with Suspenders, you need to have `node >= 14.6`.
4
-
5
- After the app is created, run the server with `bin/dev`.
@@ -1,254 +0,0 @@
1
- require "parser/current"
2
-
3
- module Suspenders
4
- module Actions
5
- class StripCommentsAction
6
- class << self
7
- def call(source)
8
- parser = Parser::CurrentRuby.new
9
-
10
- source
11
- .then { |s| strip_comments(s, parser) }
12
- .then { |s| strip_trailing_whitespace(s) }
13
- .then { |s| strip_dup_newlines(s) }
14
- .then { |s| strip_leading_scope_newlines(s, parser) }
15
- end
16
-
17
- private
18
-
19
- def strip_comments(source, parser)
20
- StripComments.call(source, parser.reset)
21
- end
22
-
23
- def strip_trailing_whitespace(source)
24
- source.gsub(/[[:blank:]]+$/, "")
25
- end
26
-
27
- def strip_dup_newlines(source)
28
- source.gsub(/\n{2,}/, "\n\n")
29
- end
30
-
31
- def strip_leading_scope_newlines(source, parser)
32
- StripLeadingScopeNewlines.call(source, parser.reset)
33
- end
34
- end
35
-
36
- # Strips full-line and inline comments from a buffer but does
37
- # not remove whitespaces or newlines after the fact. Example
38
- # input:
39
- #
40
- # MyGem.application.configure do |config|
41
- # # Full-line comment
42
- # config.option1 = :value # Inline comment
43
- # end
44
- #
45
- # The output is:
46
- #
47
- # MyGem.application.configure do |config|
48
- #
49
- # config.option1 = :value
50
- # end
51
- class StripComments
52
- class << self
53
- def call(source, parser)
54
- buffer = Parser::Source::Buffer.new(nil, source: source)
55
- rewriter = Parser::Source::TreeRewriter.new(buffer)
56
-
57
- _, comments = parser.parse_with_comments(buffer)
58
-
59
- comments.each do |comment|
60
- strip_comment(comment, buffer, rewriter)
61
- end
62
-
63
- rewriter.process
64
- end
65
-
66
- private
67
-
68
- def strip_comment(comment, buffer, rewriter)
69
- expr = comment.location.expression
70
-
71
- if full_line_comment?(expr)
72
- expr = full_line_comment_expr(expr, buffer)
73
- end
74
-
75
- rewriter.remove(expr)
76
- end
77
-
78
- def full_line_comment_expr(expr, buffer)
79
- pos = BackwardStringScanner.beginning_of_line_pos(expr, expr.begin_pos)
80
-
81
- Parser::Source::Range.new(buffer, pos, expr.end_pos + 1)
82
- end
83
-
84
- def full_line_comment?(expr)
85
- expr.source == expr.source_line.lstrip
86
- end
87
- end
88
- end
89
-
90
- # A tiny, non-stateful backward string scanner somewhat inspired
91
- # by Ruby's StringScanner. Ruby's StringScanner is unable to
92
- # seek backward on a string.
93
- class BackwardStringScanner
94
- def self.beginning_of_line_pos(expr, initial_pos)
95
- skip_before(expr, initial_pos) { |char| char == "\n" }
96
- end
97
-
98
- def self.skip_before(expr, initial_pos, &block)
99
- skip_until(expr, initial_pos, -1, &block)
100
- end
101
-
102
- def self.skip_until(expr, initial_pos, lookup_inc = 0)
103
- pos = initial_pos
104
-
105
- loop do
106
- break if pos.zero?
107
- char = expr.source_buffer.source[pos + lookup_inc]
108
- break if yield(char)
109
- pos -= 1
110
- end
111
-
112
- pos
113
- end
114
- end
115
-
116
- # The intent of this class is purely aesthetic: remove leading
117
- # newlines inside of code scopes like blocks and begin/end.
118
- # Example input:
119
- #
120
- # module MyGem
121
- #
122
- # MyGem.application.configure do |config|
123
- #
124
- # config.option1 = true
125
- #
126
- # config.option2 = false
127
- # end
128
- # end
129
- #
130
- # The output is:
131
- #
132
- # module MyGem
133
- # MyGem.application.configure do |config|
134
- # config.option1 = true
135
- #
136
- # config.option2 = false
137
- # end
138
- # end
139
- class StripLeadingScopeNewlines
140
- def self.call(source, parser)
141
- buffer = Parser::Source::Buffer.new(nil, source: source)
142
- ast = parser.parse(buffer)
143
-
144
- LeadingNewlineStripRewriter.new.rewrite(buffer, ast).lstrip
145
- end
146
-
147
- class LeadingNewlineStripRewriter < Parser::TreeRewriter
148
- def on_module(node)
149
- strip_newline_before(node.children[1])
150
- strip_newline_after(node.children.last)
151
-
152
- super
153
- end
154
-
155
- def on_class(node)
156
- strip_newline_before(node.children[2])
157
- strip_newline_after(node.children.last)
158
-
159
- super
160
- end
161
-
162
- def on_begin(node)
163
- handle_begin(node)
164
-
165
- super
166
- end
167
-
168
- def on_kwbegin(node)
169
- strip_newline_before(node.children[0])
170
- strip_newline_after(node.children.last)
171
-
172
- handle_begin(node)
173
-
174
- super
175
- end
176
-
177
- def on_block(node)
178
- strip_newline_before(node.children[2])
179
- strip_newline_after(node.children.last)
180
-
181
- super
182
- end
183
-
184
- private
185
-
186
- def handle_begin(node)
187
- strip_blank_lines_between_setter_calls(node.children)
188
-
189
- node.children.each do |child_node|
190
- send("on_#{child_node.type}", child_node)
191
- end
192
- end
193
-
194
- def strip_blank_lines_between_setter_calls(children)
195
- pairs = children.each_cons(2).to_a
196
-
197
- pairs.each do |(node_before, node_after)|
198
- if setter_call?(node_before) && setter_call?(node_after)
199
- strip_newline_before(node_after)
200
- end
201
- end
202
- end
203
-
204
- def setter_call?(node)
205
- node.children[1].to_s.end_with?("=")
206
- end
207
-
208
- def strip_newline_before(node)
209
- return if node.nil?
210
-
211
- expr = node.location.expression
212
- end_pos = find_end_pos(expr, expr.begin_pos)
213
- begin_pos = find_begin_pos(expr, end_pos)
214
-
215
- strip_source_range(expr, begin_pos, end_pos)
216
- end
217
-
218
- def strip_newline_after(node)
219
- return if node.nil?
220
-
221
- expr = node.location.expression
222
- source = expr.source_buffer.source
223
-
224
- if source[expr.end_pos + 1] == "\n"
225
- strip_source_range(expr, expr.end_pos, expr.end_pos + 1)
226
- end
227
- end
228
-
229
- def find_end_pos(expr, begin_pos)
230
- BackwardStringScanner.skip_until(expr, begin_pos) do |char|
231
- char == "\n"
232
- end
233
- end
234
-
235
- def find_begin_pos(expr, end_pos)
236
- BackwardStringScanner.skip_before(expr, end_pos) do |char|
237
- char != "\n" && char != " "
238
- end
239
- end
240
-
241
- def strip_source_range(expr, begin_pos, end_pos)
242
- remove(
243
- Parser::Source::Range.new(
244
- expr.source_buffer,
245
- begin_pos,
246
- end_pos
247
- )
248
- )
249
- end
250
- end
251
- end
252
- end
253
- end
254
- end
@@ -1,106 +0,0 @@
1
- module Suspenders
2
- module Actions
3
- def replace_in_file(relative_path, find, replace)
4
- path = File.join(destination_root, relative_path)
5
- contents = IO.read(path)
6
- unless contents.gsub!(find, replace)
7
- raise "#{find.inspect} not found in #{relative_path}"
8
- end
9
- File.write(path, contents)
10
- end
11
-
12
- def action_mailer_host(rails_env, host)
13
- config = "config.action_mailer.default_url_options = { host: #{host} }"
14
- configure_environment(rails_env, config)
15
- end
16
-
17
- def action_mailer_asset_host(rails_env, host)
18
- config = "config.action_mailer.asset_host = #{host}"
19
- configure_environment(rails_env, config)
20
- end
21
-
22
- def configure_environment(rails_env, config)
23
- inject_into_file(
24
- "config/environments/#{rails_env}.rb",
25
- "\n #{config}",
26
- before: "\nend"
27
- )
28
- end
29
-
30
- def expand_json(file, data)
31
- action ExpandJson.new(destination_root, file, data)
32
- end
33
-
34
- def gem(*args)
35
- options = args.extract_options!
36
- name, *versions = args
37
-
38
- parts = [quote(name)]
39
-
40
- if (versions = versions.any? ? versions : options.delete(:version))
41
- versions = Array(versions)
42
- versions.each do |version|
43
- parts << quote(version)
44
- end
45
- end
46
-
47
- parts << quote(options) unless options.empty?
48
-
49
- str = []
50
- str << indentation
51
- str << "gem #{parts.join(", ")}"
52
- append_file "Gemfile", %(\n#{str.join}\n), verbose: false
53
- end
54
-
55
- class ExpandJson
56
- def initialize(destination_root, file, data)
57
- @destination_root = destination_root
58
- @file = file
59
- @data = data
60
- end
61
-
62
- def invoke!
63
- write_out { |existing_json| existing_json.deep_merge(data) }
64
- end
65
-
66
- def revoke!
67
- write_out { |existing_json| hash_unmerge(existing_json, data) }
68
- end
69
-
70
- private
71
-
72
- attr_reader :destination_root, :file, :data
73
-
74
- def write_out
75
- new_json = yield(existing_json)
76
- IO.write(destination_file, JSON.pretty_generate(new_json))
77
- end
78
-
79
- def destination_file
80
- File.join(destination_root, file)
81
- end
82
-
83
- def existing_json
84
- JSON.parse(IO.read(destination_file), symbolize_names: true)
85
- rescue Errno::ENOENT
86
- {}
87
- end
88
-
89
- def hash_unmerge(hash, subhash)
90
- subhash.reduce(hash) do |acc, (k, v)|
91
- if hash.has_key?(k)
92
- if v == hash[k]
93
- acc.except(k)
94
- elsif v.is_a?(Hash)
95
- acc.merge(k => hash_unmerge(hash[k], v))
96
- else
97
- acc
98
- end
99
- else
100
- acc
101
- end
102
- end
103
- end
104
- end
105
- end
106
- end
@@ -1,136 +0,0 @@
1
- module Suspenders
2
- module Adapters
3
- class Heroku
4
- def initialize(app_builder)
5
- @app_builder = app_builder
6
- end
7
-
8
- def set_heroku_remotes
9
- remotes = <<~SHELL
10
- #{command_to_join_heroku_app("staging")}
11
- #{command_to_join_heroku_app("production")}
12
-
13
- git config heroku.remote staging
14
- SHELL
15
-
16
- app_builder.append_file "bin/setup", remotes
17
- end
18
-
19
- def create_staging_heroku_app(flags)
20
- app_name = heroku_app_name_for("staging")
21
-
22
- run_toolbelt_command "create #{app_name} #{flags}", "staging"
23
- end
24
-
25
- def create_production_heroku_app(flags)
26
- app_name = heroku_app_name_for("production")
27
-
28
- run_toolbelt_command "create #{app_name} #{flags}", "production"
29
- end
30
-
31
- def set_heroku_rails_secrets
32
- %w[staging production].each do |environment|
33
- run_toolbelt_command(
34
- "config:add SECRET_KEY_BASE=#{generate_secret}",
35
- environment
36
- )
37
- end
38
- end
39
-
40
- def set_heroku_honeybadger_env
41
- %w[staging production].each do |environment|
42
- run_toolbelt_command(
43
- "config:add HONEYBADGER_ENV=#{environment}",
44
- environment
45
- )
46
- end
47
- end
48
-
49
- def set_heroku_backup_schedule
50
- %w[staging production].each do |environment|
51
- run_toolbelt_command(
52
- "pg:backups:schedule DATABASE_URL --at '10:00 UTC'",
53
- environment
54
- )
55
- end
56
- end
57
-
58
- def create_heroku_pipeline
59
- pipelines_plugin = `heroku help | grep pipelines`
60
- if pipelines_plugin.empty?
61
- puts "You need heroku pipelines plugin. Run: brew upgrade heroku-toolbelt"
62
- exit 1
63
- end
64
-
65
- run_toolbelt_command(
66
- "pipelines:create #{heroku_app_name} \
67
- -a #{heroku_app_name}-staging --stage staging",
68
- "staging"
69
- )
70
-
71
- run_toolbelt_command(
72
- "pipelines:add #{heroku_app_name} \
73
- -a #{heroku_app_name}-production --stage production",
74
- "production"
75
- )
76
- end
77
-
78
- def set_heroku_application_host
79
- %w[staging production].each do |environment|
80
- run_toolbelt_command(
81
- "config:add APPLICATION_HOST=#{heroku_app_name}-#{environment}.herokuapp.com",
82
- environment
83
- )
84
- end
85
- end
86
-
87
- def set_heroku_buildpacks
88
- %w[staging production].each do |environment|
89
- run_toolbelt_command(
90
- "buildpacks:add --index 1 heroku/nodejs",
91
- environment
92
- )
93
- run_toolbelt_command(
94
- "buildpacks:add --index 2 heroku/ruby",
95
- environment
96
- )
97
- end
98
- end
99
-
100
- private
101
-
102
- attr_reader :app_builder
103
-
104
- def command_to_join_heroku_app(environment)
105
- heroku_app_name = heroku_app_name_for(environment)
106
- <<~SHELL
107
-
108
- if heroku apps:info --app #{heroku_app_name} > /dev/null 2>&1; then
109
- git remote add #{environment} git@heroku.com:#{heroku_app_name}.git || true
110
- printf 'You are a collaborator on the "#{heroku_app_name}" Heroku app\n'
111
- else
112
- printf 'Ask for access to the "#{heroku_app_name}" Heroku app\n'
113
- fi
114
- SHELL
115
- end
116
-
117
- def heroku_app_name
118
- app_builder.app_name.dasherize
119
- end
120
-
121
- def heroku_app_name_for(environment)
122
- "#{heroku_app_name}-#{environment}"
123
- end
124
-
125
- def generate_secret
126
- SecureRandom.hex(64)
127
- end
128
-
129
- def run_toolbelt_command(command, environment)
130
- app_builder.run(
131
- "heroku #{command} --remote #{environment}"
132
- )
133
- end
134
- end
135
- end
136
- end